c15t 1.6.0 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/dist/index.cjs +658 -98
  3. package/dist/index.d.ts +4 -1
  4. package/dist/index.d.ts.map +1 -1
  5. package/dist/index.js +632 -93
  6. package/dist/libs/fetch-consent-banner.d.ts +2 -0
  7. package/dist/libs/fetch-consent-banner.d.ts.map +1 -1
  8. package/dist/libs/gtm.d.ts +7 -0
  9. package/dist/libs/gtm.d.ts.map +1 -1
  10. package/dist/libs/has.d.ts +9 -0
  11. package/dist/libs/has.d.ts.map +1 -1
  12. package/dist/libs/iframe-blocker/core.d.ts +90 -0
  13. package/dist/libs/iframe-blocker/core.d.ts.map +1 -0
  14. package/dist/libs/iframe-blocker/index.d.ts +3 -0
  15. package/dist/libs/iframe-blocker/index.d.ts.map +1 -0
  16. package/dist/libs/iframe-blocker/store.d.ts +35 -0
  17. package/dist/libs/iframe-blocker/store.d.ts.map +1 -0
  18. package/dist/libs/iframe-blocker/types.d.ts +26 -0
  19. package/dist/libs/iframe-blocker/types.d.ts.map +1 -0
  20. package/dist/libs/save-consents.d.ts +3 -0
  21. package/dist/libs/save-consents.d.ts.map +1 -1
  22. package/dist/libs/script-loader/core.d.ts +117 -0
  23. package/dist/libs/script-loader/core.d.ts.map +1 -0
  24. package/dist/libs/script-loader/index.d.ts +10 -0
  25. package/dist/libs/script-loader/index.d.ts.map +1 -0
  26. package/dist/libs/script-loader/store.d.ts +58 -0
  27. package/dist/libs/script-loader/store.d.ts.map +1 -0
  28. package/dist/libs/script-loader/types.d.ts +160 -0
  29. package/dist/libs/script-loader/types.d.ts.map +1 -0
  30. package/dist/libs/script-loader/utils.d.ts +56 -0
  31. package/dist/libs/script-loader/utils.d.ts.map +1 -0
  32. package/dist/libs/tracking-blocker.d.ts +53 -4
  33. package/dist/libs/tracking-blocker.d.ts.map +1 -1
  34. package/dist/store.d.ts +38 -3
  35. package/dist/store.d.ts.map +1 -1
  36. package/dist/store.initial-state.d.ts +1 -8
  37. package/dist/store.initial-state.d.ts.map +1 -1
  38. package/dist/store.type.d.ts +52 -0
  39. package/dist/store.type.d.ts.map +1 -1
  40. package/dist/types/callbacks.d.ts +21 -0
  41. package/dist/types/callbacks.d.ts.map +1 -1
  42. package/dist/types/compliance.d.ts +2 -0
  43. package/dist/types/compliance.d.ts.map +1 -1
  44. package/dist/types/gdpr.d.ts +23 -0
  45. package/dist/types/gdpr.d.ts.map +1 -1
  46. package/dist/types/index.d.ts +1 -29
  47. package/dist/types/index.d.ts.map +1 -1
  48. package/dist/version.d.ts +1 -1
  49. package/package.json +3 -3
package/dist/index.js CHANGED
@@ -604,6 +604,526 @@ function configureConsentManager(options) {
604
604
  clientRegistry.set(cacheKey, client);
605
605
  return client;
606
606
  }
607
+ const gdpr_consentTypes = [
608
+ {
609
+ defaultValue: true,
610
+ description: 'These trackers are used for activities that are strictly necessary to operate or deliver the service you requested from us and, therefore, do not require you to consent.',
611
+ disabled: true,
612
+ display: true,
613
+ gdprType: 1,
614
+ name: 'necessary'
615
+ },
616
+ {
617
+ defaultValue: false,
618
+ description: 'These trackers enable basic interactions and functionalities that allow you to access selected features of our service and facilitate your communication with us.',
619
+ display: false,
620
+ gdprType: 2,
621
+ name: 'functionality'
622
+ },
623
+ {
624
+ defaultValue: false,
625
+ description: 'These trackers help us to measure traffic and analyze your behavior to improve our service.',
626
+ display: false,
627
+ gdprType: 4,
628
+ name: 'measurement'
629
+ },
630
+ {
631
+ defaultValue: false,
632
+ description: 'These trackers help us to improve the quality of your user experience and enable interactions with external content, networks, and platforms.',
633
+ display: false,
634
+ gdprType: 3,
635
+ name: 'experience'
636
+ },
637
+ {
638
+ defaultValue: false,
639
+ description: 'These trackers help us to deliver personalized ads or marketing content to you, and to measure their performance.',
640
+ display: false,
641
+ gdprType: 5,
642
+ name: 'marketing'
643
+ }
644
+ ];
645
+ const allConsentNames = gdpr_consentTypes.map((consent)=>consent.name);
646
+ function validateNonEmptyConditions(conditions, conditionType) {
647
+ if (0 === conditions.length) throw new TypeError(`${conditionType} condition cannot be empty`);
648
+ }
649
+ function evaluateCategoryCondition(category, consents) {
650
+ if (!(category in consents)) throw new Error(`Consent category "${category}" not found in consent state`);
651
+ return consents[category] || false;
652
+ }
653
+ function evaluateAndCondition(andCondition, consents) {
654
+ const andConditions = Array.isArray(andCondition) ? andCondition : [
655
+ andCondition
656
+ ];
657
+ validateNonEmptyConditions(andConditions, 'AND');
658
+ return andConditions.every((subCondition)=>evaluateConditionRecursive(subCondition, consents));
659
+ }
660
+ function evaluateOrCondition(orCondition, consents) {
661
+ const orConditions = Array.isArray(orCondition) ? orCondition : [
662
+ orCondition
663
+ ];
664
+ validateNonEmptyConditions(orConditions, 'OR');
665
+ return orConditions.some((subCondition)=>evaluateConditionRecursive(subCondition, consents));
666
+ }
667
+ function evaluateConditionRecursive(condition, consents) {
668
+ if ('string' == typeof condition) return evaluateCategoryCondition(condition, consents);
669
+ if ('object' == typeof condition && null !== condition) {
670
+ if ('and' in condition) return evaluateAndCondition(condition.and, consents);
671
+ if ('or' in condition) return evaluateOrCondition(condition.or, consents);
672
+ if ('not' in condition) return !evaluateConditionRecursive(condition.not, consents);
673
+ }
674
+ throw new TypeError(`Invalid condition structure: ${JSON.stringify(condition)}`);
675
+ }
676
+ function has_has(condition, consents) {
677
+ return evaluateConditionRecursive(condition, consents);
678
+ }
679
+ function extractConsentNamesFromCondition(condition) {
680
+ const categories = new Set();
681
+ function recurse(cond) {
682
+ if ('string' == typeof cond) return void categories.add(cond);
683
+ if ('object' == typeof cond && null !== cond) {
684
+ if ('and' in cond) {
685
+ const conditions = Array.isArray(cond.and) ? cond.and : [
686
+ cond.and
687
+ ];
688
+ conditions.forEach(recurse);
689
+ } else if ('or' in cond) {
690
+ const conditions = Array.isArray(cond.or) ? cond.or : [
691
+ cond.or
692
+ ];
693
+ conditions.forEach(recurse);
694
+ } else if ('not' in cond) recurse(cond.not);
695
+ }
696
+ }
697
+ recurse(condition);
698
+ return Array.from(categories);
699
+ }
700
+ function createDefaultConsentState() {
701
+ return {
702
+ experience: false,
703
+ functionality: false,
704
+ marketing: false,
705
+ measurement: false,
706
+ necessary: true
707
+ };
708
+ }
709
+ function determineRequiredConsent(iframe) {
710
+ const categoryAttr = iframe.getAttribute('data-category');
711
+ if (!categoryAttr) return;
712
+ if (!allConsentNames.includes(categoryAttr)) throw new Error(`Invalid category attribute "${categoryAttr}" on iframe. Must be one of: ${allConsentNames.join(', ')}`);
713
+ return categoryAttr;
714
+ }
715
+ function processIframeElement(iframe, consents) {
716
+ const dataSrc = iframe.getAttribute('data-src');
717
+ const requiredConsent = determineRequiredConsent(iframe);
718
+ if (!requiredConsent) return;
719
+ const hasConsent = has_has(requiredConsent, consents);
720
+ if (hasConsent) {
721
+ if (dataSrc && !iframe.src) {
722
+ iframe.src = dataSrc;
723
+ iframe.removeAttribute('data-src');
724
+ }
725
+ } else if (iframe.src) iframe.removeAttribute('src');
726
+ }
727
+ function createIframeBlocker(config = {}, initialConsents) {
728
+ const blockerConfig = {
729
+ disableAutomaticBlocking: false,
730
+ ...config
731
+ };
732
+ let consents = initialConsents || createDefaultConsentState();
733
+ function processIframes() {
734
+ const iframes = document.querySelectorAll('iframe');
735
+ iframes.forEach((iframe)=>{
736
+ processIframeElement(iframe, consents);
737
+ });
738
+ }
739
+ function setupMutationObserver() {
740
+ const observer = new MutationObserver((mutations)=>{
741
+ mutations.forEach((mutation)=>{
742
+ mutation.addedNodes.forEach((node)=>{
743
+ if (node.nodeType === Node.ELEMENT_NODE) {
744
+ const element = node;
745
+ if (element.tagName && 'IFRAME' === element.tagName.toUpperCase()) processIframeElement(element, consents);
746
+ const iframes = element.querySelectorAll?.('iframe');
747
+ if (iframes) iframes.forEach((iframe)=>{
748
+ processIframeElement(iframe, consents);
749
+ });
750
+ }
751
+ });
752
+ });
753
+ });
754
+ observer.observe(document.body, {
755
+ childList: true,
756
+ subtree: true
757
+ });
758
+ return observer;
759
+ }
760
+ let mutationObserver = null;
761
+ if (!blockerConfig.disableAutomaticBlocking) {
762
+ processIframes();
763
+ mutationObserver = setupMutationObserver();
764
+ }
765
+ return {
766
+ updateConsents: (newConsents)=>{
767
+ consents = {
768
+ ...consents,
769
+ ...newConsents
770
+ };
771
+ processIframes();
772
+ },
773
+ processIframes,
774
+ destroy: ()=>{
775
+ if (mutationObserver) {
776
+ mutationObserver.disconnect();
777
+ mutationObserver = null;
778
+ }
779
+ }
780
+ };
781
+ }
782
+ function getIframeConsentCategories() {
783
+ if ('undefined' == typeof document) return [];
784
+ const iframes = document.querySelectorAll('iframe[data-category]');
785
+ const categories = new Set();
786
+ iframes.forEach((iframe)=>{
787
+ const categoryAttr = iframe.getAttribute('data-category');
788
+ if (!categoryAttr) return;
789
+ const category = categoryAttr.trim();
790
+ if (allConsentNames.includes(category)) categories.add(category);
791
+ });
792
+ return Array.from(categories);
793
+ }
794
+ function processAllIframes(consents) {
795
+ if ('undefined' == typeof document) return;
796
+ const iframes = document.querySelectorAll('iframe');
797
+ iframes.forEach((iframe)=>{
798
+ processIframeElement(iframe, consents);
799
+ });
800
+ }
801
+ function setupIframeObserver(getConsents, onCategoriesDiscovered) {
802
+ const observer = new MutationObserver((mutations)=>{
803
+ const currentConsents = getConsents();
804
+ let hasNewCategories = false;
805
+ mutations.forEach((mutation)=>{
806
+ mutation.addedNodes.forEach((node)=>{
807
+ if (node.nodeType === Node.ELEMENT_NODE) {
808
+ const element = node;
809
+ if (element.tagName && 'IFRAME' === element.tagName.toUpperCase()) {
810
+ processIframeElement(element, currentConsents);
811
+ if (element.hasAttribute('data-category')) hasNewCategories = true;
812
+ }
813
+ const iframes = element.querySelectorAll?.('iframe');
814
+ if (iframes && iframes.length > 0) iframes.forEach((iframe)=>{
815
+ processIframeElement(iframe, currentConsents);
816
+ if (iframe.hasAttribute('data-category')) hasNewCategories = true;
817
+ });
818
+ }
819
+ });
820
+ });
821
+ if (hasNewCategories && onCategoriesDiscovered) {
822
+ const categories = getIframeConsentCategories();
823
+ if (categories.length > 0) onCategoriesDiscovered(categories);
824
+ }
825
+ });
826
+ observer.observe(document.body, {
827
+ childList: true,
828
+ subtree: true
829
+ });
830
+ return observer;
831
+ }
832
+ function generateRandomScriptId() {
833
+ if ('undefined' != typeof crypto && crypto.randomUUID) return crypto.randomUUID().replace(/-/g, '').substring(0, 8);
834
+ if ('undefined' != typeof crypto && crypto.getRandomValues) {
835
+ const array = new Uint8Array(4);
836
+ crypto.getRandomValues(array);
837
+ return Array.from(array, (byte)=>byte.toString(36)).join('').padEnd(8, '0').substring(0, 8);
838
+ }
839
+ const randomStr = Math.random().toString(36).substring(2);
840
+ return randomStr.padEnd(8, '0').substring(0, 8);
841
+ }
842
+ function getScriptElementId(scriptId, anonymizeId, scriptIdMap) {
843
+ if (anonymizeId) {
844
+ if (scriptIdMap[scriptId]) return scriptIdMap[scriptId];
845
+ scriptIdMap[scriptId] = generateRandomScriptId();
846
+ return scriptIdMap[scriptId];
847
+ }
848
+ return `c15t-script-${scriptId}`;
849
+ }
850
+ const loadedScripts = new Map();
851
+ function hasLoadedScript(src) {
852
+ return loadedScripts.has(src);
853
+ }
854
+ function getLoadedScript(src) {
855
+ return loadedScripts.get(src);
856
+ }
857
+ function setLoadedScript(src, element) {
858
+ loadedScripts.set(src, element);
859
+ }
860
+ function deleteLoadedScript(src) {
861
+ loadedScripts.delete(src);
862
+ }
863
+ function utils_getLoadedScriptsSnapshot() {
864
+ return loadedScripts;
865
+ }
866
+ function loadScripts(scripts, consents, scriptIdMap = {}) {
867
+ const loadedScriptIds = [];
868
+ scripts.forEach((script)=>{
869
+ if (!script.alwaysLoad && !has_has(script.category, consents)) return;
870
+ if (hasLoadedScript(script.id)) return void script.onConsentChange?.({
871
+ id: script.id,
872
+ elementId: getScriptElementId(script.id, false !== script.anonymizeId, scriptIdMap),
873
+ hasConsent: has_has(script.category, consents),
874
+ consents
875
+ });
876
+ if (script.src && script.textContent) throw new Error(`Script '${script.id}' cannot have both 'src' and 'textContent'. Choose one.`);
877
+ if (!script.src && !script.textContent && !script.callbackOnly) throw new Error(`Script '${script.id}' must have either 'src', 'textContent', or 'callbackOnly' set to true.`);
878
+ if (true === script.callbackOnly) {
879
+ const shouldAnonymize = false !== script.anonymizeId;
880
+ const elementId = getScriptElementId(script.id, shouldAnonymize, scriptIdMap);
881
+ const callbackInfo = {
882
+ id: script.id,
883
+ elementId,
884
+ consents,
885
+ hasConsent: has_has(script.category, consents)
886
+ };
887
+ if (script.onBeforeLoad) script.onBeforeLoad(callbackInfo);
888
+ if (script.onLoad) script.onLoad(callbackInfo);
889
+ setLoadedScript(script.id, null);
890
+ loadedScriptIds.push(script.id);
891
+ return;
892
+ }
893
+ const shouldAnonymize = false !== script.anonymizeId;
894
+ const elementId = getScriptElementId(script.id, shouldAnonymize, scriptIdMap);
895
+ if (true === script.persistAfterConsentRevoked) {
896
+ const existingElement = document.getElementById(elementId);
897
+ if (existingElement) {
898
+ const callbackInfo = {
899
+ id: script.id,
900
+ hasConsent: has_has(script.category, consents),
901
+ elementId,
902
+ consents,
903
+ element: existingElement
904
+ };
905
+ script.onConsentChange?.(callbackInfo);
906
+ script.onLoad?.(callbackInfo);
907
+ setLoadedScript(script.id, existingElement);
908
+ loadedScriptIds.push(script.id);
909
+ return;
910
+ }
911
+ }
912
+ const scriptElement = document.createElement("script");
913
+ scriptElement.id = elementId;
914
+ if (script.src) scriptElement.src = script.src;
915
+ else if (script.textContent) scriptElement.textContent = script.textContent;
916
+ if (script.fetchPriority) scriptElement.fetchPriority = script.fetchPriority;
917
+ if (script.async) scriptElement.async = true;
918
+ if (script.defer) scriptElement.defer = true;
919
+ if (script.nonce) scriptElement.nonce = script.nonce;
920
+ if (script.attributes) Object.entries(script.attributes).forEach(([key, value])=>{
921
+ scriptElement.setAttribute(key, value);
922
+ });
923
+ const callbackInfo = {
924
+ id: script.id,
925
+ hasConsent: has_has(script.category, consents),
926
+ elementId,
927
+ consents,
928
+ element: scriptElement
929
+ };
930
+ if (script.onLoad) if (script.textContent) setTimeout(()=>{
931
+ script.onLoad?.({
932
+ ...callbackInfo
933
+ });
934
+ }, 0);
935
+ else scriptElement.addEventListener('load', ()=>{
936
+ script.onLoad?.({
937
+ ...callbackInfo
938
+ });
939
+ });
940
+ if (script.onError) script.textContent || scriptElement.addEventListener('error', ()=>{
941
+ script.onError?.({
942
+ ...callbackInfo,
943
+ error: new Error(`Failed to load script: ${script.src}`)
944
+ });
945
+ });
946
+ if (script.onBeforeLoad) script.onBeforeLoad(callbackInfo);
947
+ document.head.appendChild(scriptElement);
948
+ setLoadedScript(script.id, scriptElement);
949
+ loadedScriptIds.push(script.id);
950
+ });
951
+ return loadedScriptIds;
952
+ }
953
+ function unloadScripts(scripts, consents, scriptIdMap = {}) {
954
+ const unloadedScriptIds = [];
955
+ scripts.forEach((script)=>{
956
+ if (!hasLoadedScript(script.id)) return;
957
+ if (script.alwaysLoad) return;
958
+ if (!has_has(script.category, consents)) {
959
+ const scriptElement = getLoadedScript(script.id);
960
+ const elementId = scriptIdMap[script.id] || `c15t-script-${script.id}`;
961
+ if (true === script.callbackOnly || null === scriptElement) {
962
+ const callbackInfo = {
963
+ id: script.id,
964
+ elementId,
965
+ consents,
966
+ hasConsent: has_has(script.category, consents)
967
+ };
968
+ if (script.onDelete) script.onDelete(callbackInfo);
969
+ deleteLoadedScript(script.id);
970
+ unloadedScriptIds.push(script.id);
971
+ } else if (scriptElement) {
972
+ const callbackInfo = {
973
+ id: script.id,
974
+ elementId,
975
+ consents,
976
+ hasConsent: has_has(script.category, consents),
977
+ element: scriptElement
978
+ };
979
+ if (script.onDelete) script.onDelete(callbackInfo);
980
+ if (script.persistAfterConsentRevoked) {
981
+ deleteLoadedScript(script.id);
982
+ unloadedScriptIds.push(script.id);
983
+ } else {
984
+ scriptElement.remove();
985
+ deleteLoadedScript(script.id);
986
+ unloadedScriptIds.push(script.id);
987
+ }
988
+ }
989
+ }
990
+ });
991
+ return unloadedScriptIds;
992
+ }
993
+ function core_updateScripts(scripts, consents, scriptIdMap = {}) {
994
+ const unloaded = unloadScripts(scripts, consents, scriptIdMap);
995
+ const loaded = loadScripts(scripts, consents, scriptIdMap);
996
+ return {
997
+ loaded,
998
+ unloaded
999
+ };
1000
+ }
1001
+ function isScriptLoaded(scriptId) {
1002
+ return hasLoadedScript(scriptId);
1003
+ }
1004
+ function getLoadedScriptIds() {
1005
+ return Array.from(utils_getLoadedScriptsSnapshot().keys());
1006
+ }
1007
+ function reloadScript(scriptId, scripts, consents, scriptIdMap = {}) {
1008
+ const script = scripts.find((s)=>s.id === scriptId);
1009
+ if (!script) return false;
1010
+ if (hasLoadedScript(scriptId)) {
1011
+ const scriptElement = getLoadedScript(scriptId);
1012
+ const elementId = scriptIdMap[scriptId] || `c15t-script-${scriptId}`;
1013
+ if (true === script.callbackOnly || null === scriptElement) {
1014
+ const callbackInfo = {
1015
+ id: scriptId,
1016
+ elementId,
1017
+ consents,
1018
+ hasConsent: has_has(script.category, consents)
1019
+ };
1020
+ if (script.onDelete) script.onDelete(callbackInfo);
1021
+ deleteLoadedScript(scriptId);
1022
+ } else if (scriptElement) {
1023
+ const callbackInfo = {
1024
+ id: scriptId,
1025
+ elementId,
1026
+ consents,
1027
+ hasConsent: has_has(script.category, consents),
1028
+ element: scriptElement
1029
+ };
1030
+ if (script.onDelete) script.onDelete(callbackInfo);
1031
+ if (!script.persistAfterConsentRevoked) scriptElement.remove();
1032
+ deleteLoadedScript(scriptId);
1033
+ }
1034
+ }
1035
+ if (!script.alwaysLoad && !has_has(script.category, consents)) return false;
1036
+ loadScripts([
1037
+ script
1038
+ ], consents, scriptIdMap);
1039
+ return true;
1040
+ }
1041
+ function createScriptManager(getState, setState, trackingBlocker) {
1042
+ const updateScriptsFn = ()=>{
1043
+ const { scripts, consents, scriptIdMap } = getState();
1044
+ const result = core_updateScripts(scripts, consents, scriptIdMap);
1045
+ const newLoadedScripts = {
1046
+ ...getState().loadedScripts
1047
+ };
1048
+ result.loaded.forEach((id)=>{
1049
+ newLoadedScripts[id] = true;
1050
+ });
1051
+ result.unloaded.forEach((id)=>{
1052
+ newLoadedScripts[id] = false;
1053
+ });
1054
+ setState({
1055
+ loadedScripts: newLoadedScripts
1056
+ });
1057
+ return result;
1058
+ };
1059
+ return {
1060
+ updateScripts: ()=>updateScriptsFn(),
1061
+ setScripts: (scripts)=>{
1062
+ if (trackingBlocker) trackingBlocker.destroy();
1063
+ const state = getState();
1064
+ const newScriptIdMap = {
1065
+ ...state.scriptIdMap
1066
+ };
1067
+ scripts.forEach((script)=>{
1068
+ if (false !== script.anonymizeId) newScriptIdMap[script.id] = generateRandomScriptId();
1069
+ });
1070
+ const newCategories = scripts.flatMap((script)=>extractConsentNamesFromCondition(script.category));
1071
+ const allCategories = [
1072
+ ...new Set([
1073
+ ...state.gdprTypes,
1074
+ ...newCategories
1075
+ ])
1076
+ ];
1077
+ setState({
1078
+ scripts: [
1079
+ ...state.scripts,
1080
+ ...scripts
1081
+ ],
1082
+ scriptIdMap: newScriptIdMap,
1083
+ gdprTypes: allCategories
1084
+ });
1085
+ updateScriptsFn();
1086
+ },
1087
+ removeScript: (scriptId)=>{
1088
+ const state = getState();
1089
+ const script = state.scripts.find((s)=>s.id === scriptId);
1090
+ if (hasLoadedScript(scriptId)) {
1091
+ const scriptElement = getLoadedScript(scriptId);
1092
+ if (scriptElement) {
1093
+ const elementId = state.scriptIdMap[scriptId] || `c15t-script-${scriptId}`;
1094
+ const callbackInfo = {
1095
+ id: scriptId,
1096
+ elementId,
1097
+ consents: state.consents,
1098
+ element: scriptElement,
1099
+ hasConsent: has_has(script?.category ?? 'necessary', state.consents)
1100
+ };
1101
+ if (script?.onDelete) script.onDelete(callbackInfo);
1102
+ scriptElement.remove();
1103
+ deleteLoadedScript(scriptId);
1104
+ }
1105
+ }
1106
+ const newScriptIdMap = {
1107
+ ...state.scriptIdMap
1108
+ };
1109
+ delete newScriptIdMap[scriptId];
1110
+ setState({
1111
+ scripts: state.scripts.filter((script)=>script.id !== scriptId),
1112
+ loadedScripts: {
1113
+ ...state.loadedScripts,
1114
+ [scriptId]: false
1115
+ },
1116
+ scriptIdMap: newScriptIdMap
1117
+ });
1118
+ },
1119
+ reloadScript: (scriptId)=>{
1120
+ const state = getState();
1121
+ return reloadScript(scriptId, state.scripts, state.consents, state.scriptIdMap);
1122
+ },
1123
+ isScriptLoaded: (scriptId)=>isScriptLoaded(scriptId),
1124
+ getLoadedScriptIds: ()=>getLoadedScriptIds()
1125
+ };
1126
+ }
607
1127
  const DEFAULT_DOMAIN_CONSENT_MAP = {
608
1128
  'www.google-analytics.com': 'measurement',
609
1129
  'analytics.google.com': 'measurement',
@@ -749,7 +1269,7 @@ const DEFAULT_DOMAIN_CONSENT_MAP = {
749
1269
  'app.contentsquare.com': 'experience'
750
1270
  };
751
1271
  const tracking_domains = DEFAULT_DOMAIN_CONSENT_MAP;
752
- function createDefaultConsentState() {
1272
+ function tracking_blocker_createDefaultConsentState() {
753
1273
  return {
754
1274
  experience: false,
755
1275
  functionality: false,
@@ -769,7 +1289,7 @@ function createTrackingBlocker(config = {}, initialConsents) {
769
1289
  ...config.domainConsentMap
770
1290
  }
771
1291
  };
772
- let consents = initialConsents || createDefaultConsentState();
1292
+ let consents = initialConsents || tracking_blocker_createDefaultConsentState();
773
1293
  const originalFetch = window.fetch;
774
1294
  const originalXHR = window.XMLHttpRequest;
775
1295
  function normalizeDomain(domain) {
@@ -849,7 +1369,7 @@ function hasConsentFor(consentType, consents, honorDoNotTrack) {
849
1369
  const effectiveConsents = getEffectiveConsents(consents, honorDoNotTrack);
850
1370
  return effectiveConsents[consentType] || false;
851
1371
  }
852
- function consent_utils_hasConsented(consentInfo) {
1372
+ function hasConsented(consentInfo) {
853
1373
  return null !== consentInfo;
854
1374
  }
855
1375
  function checkLocalStorageAccess(set) {
@@ -868,16 +1388,17 @@ function checkLocalStorageAccess(set) {
868
1388
  }
869
1389
  return false;
870
1390
  }
871
- function updateStore(data, { set, get, initialTranslationConfig }, hasLocalStorageAccess) {
1391
+ function updateStore(data, { set, get, initialTranslationConfig, trackingBlocker }, hasLocalStorageAccess) {
872
1392
  const { consentInfo, ignoreGeoLocation, callbacks, setDetectedCountry } = get();
873
1393
  const { translations, location, showConsentBanner } = data;
1394
+ const shouldAutoGrantConsents = data.jurisdiction?.code === 'NONE' && !data.showConsentBanner;
874
1395
  const updatedStore = {
875
1396
  isLoadingConsentInfo: false,
876
1397
  branding: data.branding ?? 'c15t',
877
1398
  ...null === consentInfo ? {
878
1399
  showPopup: showConsentBanner && hasLocalStorageAccess || ignoreGeoLocation
879
1400
  } : {},
880
- ...data.jurisdiction?.code === 'NONE' && !data.showConsentBanner && {
1401
+ ...shouldAutoGrantConsents && {
881
1402
  consents: {
882
1403
  necessary: true,
883
1404
  functionality: true,
@@ -894,7 +1415,8 @@ function updateStore(data, { set, get, initialTranslationConfig }, hasLocalStora
894
1415
  },
895
1416
  jurisdictionInfo: data.jurisdiction
896
1417
  };
897
- if (translations?.language && translations?.translations) {
1418
+ translations?.language && translations?.translations;
1419
+ {
898
1420
  const translationConfig = (0, __WEBPACK_EXTERNAL_MODULE__c15t_translations_cdae900b__.prepareTranslationConfig)({
899
1421
  translations: {
900
1422
  [translations.language]: translations.translations
@@ -908,6 +1430,15 @@ function updateStore(data, { set, get, initialTranslationConfig }, hasLocalStora
908
1430
  updatedStore.hasFetchedBanner = true;
909
1431
  updatedStore.lastBannerFetchData = data;
910
1432
  set(updatedStore);
1433
+ if (shouldAutoGrantConsents) callbacks?.onConsentSet?.({
1434
+ preferences: {
1435
+ necessary: true,
1436
+ functionality: true,
1437
+ experience: true,
1438
+ marketing: true,
1439
+ measurement: true
1440
+ }
1441
+ });
911
1442
  callbacks?.onBannerFetched?.({
912
1443
  showConsentBanner: data.showConsentBanner,
913
1444
  jurisdiction: data.jurisdiction,
@@ -917,24 +1448,26 @@ function updateStore(data, { set, get, initialTranslationConfig }, hasLocalStora
917
1448
  translations: translations.translations
918
1449
  }
919
1450
  });
1451
+ trackingBlocker?.updateConsents(get().consents);
1452
+ get().updateScripts();
920
1453
  }
921
1454
  async function fetchConsentBannerInfo(config) {
922
1455
  const { get, set, manager, initialData } = config;
923
- const { hasConsented, callbacks } = get();
924
- if ('undefined' == typeof window || hasConsented()) return void set({
925
- isLoadingConsentInfo: false
926
- });
1456
+ const { callbacks } = get();
1457
+ if ('undefined' == typeof window) return;
927
1458
  const hasLocalStorageAccess = checkLocalStorageAccess(set);
928
1459
  if (!hasLocalStorageAccess) return;
929
1460
  set({
930
1461
  isLoadingConsentInfo: true
931
1462
  });
932
- if (initialData) {
1463
+ if (initialData) try {
933
1464
  const showConsentBanner = await initialData;
934
1465
  if (showConsentBanner) {
935
1466
  updateStore(showConsentBanner, config, true);
936
1467
  return showConsentBanner;
937
1468
  }
1469
+ } catch (error) {
1470
+ throw error;
938
1471
  }
939
1472
  try {
940
1473
  const { data, error } = await manager.showConsentBanner({
@@ -1038,78 +1571,49 @@ function updateGTMConsent(consentState) {
1038
1571
  if (!window.gtag) return;
1039
1572
  window.gtag('consent', 'update', mapConsentStateToGTM(consentState));
1040
1573
  }
1041
- function validateNonEmptyConditions(conditions, conditionType) {
1042
- if (0 === conditions.length) throw new TypeError(`${conditionType} condition cannot be empty`);
1043
- }
1044
- function evaluateCategoryCondition(category, consents) {
1045
- if (!(category in consents)) throw new Error(`Consent category "${category}" not found in consent state`);
1046
- return consents[category] || false;
1047
- }
1048
- function evaluateAndCondition(andCondition, consents) {
1049
- const andConditions = Array.isArray(andCondition) ? andCondition : [
1050
- andCondition
1051
- ];
1052
- validateNonEmptyConditions(andConditions, 'AND');
1053
- return andConditions.every((subCondition)=>evaluateConditionRecursive(subCondition, consents));
1054
- }
1055
- function evaluateOrCondition(orCondition, consents) {
1056
- const orConditions = Array.isArray(orCondition) ? orCondition : [
1057
- orCondition
1058
- ];
1059
- validateNonEmptyConditions(orConditions, 'OR');
1060
- return orConditions.some((subCondition)=>evaluateConditionRecursive(subCondition, consents));
1061
- }
1062
- function evaluateConditionRecursive(condition, consents) {
1063
- if ('string' == typeof condition) return evaluateCategoryCondition(condition, consents);
1064
- if ('object' == typeof condition && null !== condition) {
1065
- if ('and' in condition) return evaluateAndCondition(condition.and, consents);
1066
- if ('or' in condition) return evaluateOrCondition(condition.or, consents);
1067
- if ('not' in condition) return !evaluateConditionRecursive(condition.not, consents);
1068
- }
1069
- throw new TypeError(`Invalid condition structure: ${JSON.stringify(condition)}`);
1070
- }
1071
- function has(condition, consents) {
1072
- return evaluateConditionRecursive(condition, consents);
1574
+ function createIframeManager(get, _set) {
1575
+ let observer = null;
1576
+ let isInitialized = false;
1577
+ return {
1578
+ initializeIframeBlocker: ()=>{
1579
+ if (isInitialized) return;
1580
+ if ('undefined' == typeof document) return;
1581
+ const state = get();
1582
+ if (state.iframeBlockerConfig?.disableAutomaticBlocking) return;
1583
+ const discoverAndRegisterCategories = ()=>{
1584
+ const iframeCategories = getIframeConsentCategories();
1585
+ if (iframeCategories.length > 0) get().updateConsentCategories(iframeCategories);
1586
+ };
1587
+ if ('loading' === document.readyState) document.addEventListener('DOMContentLoaded', discoverAndRegisterCategories);
1588
+ else discoverAndRegisterCategories();
1589
+ setTimeout(discoverAndRegisterCategories, 100);
1590
+ processAllIframes(state.consents);
1591
+ observer = setupIframeObserver(()=>get().consents, (categories)=>get().updateConsentCategories(categories));
1592
+ isInitialized = true;
1593
+ },
1594
+ updateIframeConsents: ()=>{
1595
+ if (!isInitialized) return;
1596
+ if ('undefined' == typeof document) return;
1597
+ const state = get();
1598
+ const { consents, iframeBlockerConfig } = state;
1599
+ if (iframeBlockerConfig?.disableAutomaticBlocking) return;
1600
+ processAllIframes(consents);
1601
+ },
1602
+ destroyIframeBlocker: ()=>{
1603
+ if (!isInitialized) return;
1604
+ if ('undefined' == typeof document) return;
1605
+ const state = get();
1606
+ const { iframeBlockerConfig } = state;
1607
+ if (iframeBlockerConfig?.disableAutomaticBlocking) return;
1608
+ if (observer) {
1609
+ observer.disconnect();
1610
+ observer = null;
1611
+ }
1612
+ isInitialized = false;
1613
+ }
1614
+ };
1073
1615
  }
1074
- const gdpr_consentTypes = [
1075
- {
1076
- defaultValue: true,
1077
- description: 'These trackers are used for activities that are strictly necessary to operate or deliver the service you requested from us and, therefore, do not require you to consent.',
1078
- disabled: true,
1079
- display: true,
1080
- gdprType: 1,
1081
- name: 'necessary'
1082
- },
1083
- {
1084
- defaultValue: false,
1085
- description: 'These trackers enable basic interactions and functionalities that allow you to access selected features of our service and facilitate your communication with us.',
1086
- display: false,
1087
- gdprType: 2,
1088
- name: 'functionality'
1089
- },
1090
- {
1091
- defaultValue: false,
1092
- description: 'These trackers help us to measure traffic and analyze your behavior to improve our service.',
1093
- display: false,
1094
- gdprType: 4,
1095
- name: 'measurement'
1096
- },
1097
- {
1098
- defaultValue: false,
1099
- description: 'These trackers help us to improve the quality of your user experience and enable interactions with external content, networks, and platforms.',
1100
- display: false,
1101
- gdprType: 3,
1102
- name: 'experience'
1103
- },
1104
- {
1105
- defaultValue: false,
1106
- description: 'These trackers help us to deliver personalized ads or marketing content to you, and to measure their performance.',
1107
- display: false,
1108
- gdprType: 5,
1109
- name: 'marketing'
1110
- }
1111
- ];
1112
- const version = '1.6.0';
1616
+ const version = '1.7.0';
1113
1617
  const STORAGE_KEY = 'privacy-consent-storage';
1114
1618
  const initialState = {
1115
1619
  config: {
@@ -1166,6 +1670,9 @@ const initialState = {
1166
1670
  translationConfig: defaultTranslationConfig,
1167
1671
  includeNonDisplayedConsents: false,
1168
1672
  consentTypes: gdpr_consentTypes,
1673
+ iframeBlockerConfig: {
1674
+ disableAutomaticBlocking: false
1675
+ },
1169
1676
  ignoreGeoLocation: false,
1170
1677
  privacySettings: {
1171
1678
  honorDoNotTrack: true
@@ -1183,13 +1690,17 @@ const initialState = {
1183
1690
  setLocationInfo: ()=>{},
1184
1691
  getDisplayedConsents: ()=>[],
1185
1692
  hasConsented: ()=>false,
1186
- setTranslationConfig: ()=>{}
1693
+ setTranslationConfig: ()=>{},
1694
+ scripts: [],
1695
+ loadedScripts: {},
1696
+ scriptIdMap: {}
1187
1697
  };
1188
1698
  async function saveConsents({ manager, type, get, set, trackingBlocker }) {
1189
- const { callbacks, selectedConsents, consents, consentTypes } = get();
1699
+ const { callbacks, selectedConsents, consents, consentTypes, updateScripts, updateIframeConsents, gdprTypes } = get();
1190
1700
  const newConsents = selectedConsents ?? consents ?? {};
1191
- if ('all' === type) for (const consent of consentTypes)newConsents[consent.name] = true;
1192
- else if ('necessary' === type) for (const consent of consentTypes)newConsents[consent.name] = 'necessary' === consent.name;
1701
+ if ('all' === type) {
1702
+ for (const consent of consentTypes)if (gdprTypes.includes(consent.name)) newConsents[consent.name] = true;
1703
+ } else if ('necessary' === type) for (const consent of consentTypes)newConsents[consent.name] = 'necessary' === consent.name;
1193
1704
  const consentInfo = {
1194
1705
  time: Date.now(),
1195
1706
  type: type
@@ -1202,7 +1713,9 @@ async function saveConsents({ manager, type, get, set, trackingBlocker }) {
1202
1713
  });
1203
1714
  await new Promise((resolve)=>setTimeout(resolve, 0));
1204
1715
  trackingBlocker?.updateConsents(newConsents);
1716
+ updateIframeConsents();
1205
1717
  updateGTMConsent(newConsents);
1718
+ updateScripts();
1206
1719
  try {
1207
1720
  localStorage.setItem(STORAGE_KEY, JSON.stringify({
1208
1721
  consents: newConsents,
@@ -1247,13 +1760,17 @@ const getStoredConsent = ()=>{
1247
1760
  const createConsentManagerStore = (manager, options = {})=>{
1248
1761
  const { namespace = 'c15tStore', trackingBlockerConfig, isConsentDomain = false, translationConfig } = options;
1249
1762
  const storedConsent = getStoredConsent();
1250
- const trackingBlocker = 'undefined' != typeof window ? createTrackingBlocker(trackingBlockerConfig || {}, storedConsent?.consents || initialState.consents) : null;
1763
+ const shouldDisableTrackingBlocker = options.scripts && options.scripts.length > 0;
1764
+ const trackingBlocker = 'undefined' == typeof window || shouldDisableTrackingBlocker ? null : createTrackingBlocker(trackingBlockerConfig || {}, storedConsent?.consents || initialState.consents);
1251
1765
  const store = (0, __WEBPACK_EXTERNAL_MODULE_zustand_vanilla_8a8e4ffb__.createStore)((set, get)=>({
1252
1766
  ...initialState,
1767
+ gdprTypes: options.initialGdprTypes ?? initialState.gdprTypes,
1253
1768
  ignoreGeoLocation: options.ignoreGeoLocation ?? false,
1254
1769
  config: options.config ?? initialState.config,
1770
+ iframeBlockerConfig: options.iframeBlockerConfig ?? initialState.iframeBlockerConfig,
1255
1771
  isConsentDomain,
1256
1772
  callbacks: options.callbacks ?? initialState.callbacks,
1773
+ scripts: options.scripts ?? initialState.scripts,
1257
1774
  translationConfig: translationConfig || initialState.translationConfig,
1258
1775
  ...storedConsent ? {
1259
1776
  consents: storedConsent.consents,
@@ -1352,6 +1869,9 @@ const createConsentManagerStore = (manager, options = {})=>{
1352
1869
  [name]: callback
1353
1870
  }
1354
1871
  }));
1872
+ if ('onConsentSet' === name && callback && 'function' == typeof callback) callback?.({
1873
+ preferences: currentState.consents
1874
+ });
1355
1875
  if ('onBannerFetched' === name && currentState.hasFetchedBanner && currentState.lastBannerFetchData && callback && 'function' == typeof callback) {
1356
1876
  const { lastBannerFetchData } = currentState;
1357
1877
  callback?.({
@@ -1376,7 +1896,8 @@ const createConsentManagerStore = (manager, options = {})=>{
1376
1896
  initialData: options._initialData,
1377
1897
  initialTranslationConfig: options.initialTranslationConfig,
1378
1898
  get,
1379
- set
1899
+ set,
1900
+ trackingBlocker
1380
1901
  }),
1381
1902
  getDisplayedConsents: ()=>{
1382
1903
  const { gdprTypes, consentTypes } = get();
@@ -1384,7 +1905,7 @@ const createConsentManagerStore = (manager, options = {})=>{
1384
1905
  },
1385
1906
  hasConsented: ()=>{
1386
1907
  const { consentInfo } = get();
1387
- return consent_utils_hasConsented(consentInfo);
1908
+ return hasConsented(consentInfo);
1388
1909
  },
1389
1910
  getEffectiveConsents: ()=>{
1390
1911
  const { consents, privacySettings } = get();
@@ -1396,14 +1917,29 @@ const createConsentManagerStore = (manager, options = {})=>{
1396
1917
  },
1397
1918
  has: (condition)=>{
1398
1919
  const { consents } = get();
1399
- return has(condition, consents);
1920
+ return has_has(condition, consents);
1400
1921
  },
1401
1922
  setTranslationConfig: (config)=>{
1402
1923
  set({
1403
1924
  translationConfig: config
1404
1925
  });
1405
- }
1926
+ },
1927
+ updateConsentCategories: (newCategories)=>{
1928
+ const allCategories = [
1929
+ ...new Set([
1930
+ ...get().gdprTypes,
1931
+ ...newCategories
1932
+ ])
1933
+ ];
1934
+ set({
1935
+ gdprTypes: allCategories
1936
+ });
1937
+ },
1938
+ ...createScriptManager(get, set, trackingBlocker),
1939
+ ...createIframeManager(get, set)
1406
1940
  }));
1941
+ store.getState().initializeIframeBlocker();
1942
+ if (options.scripts && options.scripts.length > 0) store.getState().updateConsentCategories(options.scripts.flatMap((script)=>extractConsentNamesFromCondition(script.category)));
1407
1943
  if ('undefined' != typeof window) {
1408
1944
  window[namespace] = store;
1409
1945
  if (options.unstable_googleTagManager) try {
@@ -1414,7 +1950,10 @@ const createConsentManagerStore = (manager, options = {})=>{
1414
1950
  } catch (e) {
1415
1951
  console.error('Failed to setup Google Tag Manager:', e);
1416
1952
  }
1417
- if (!getStoredConsent()) store.getState().fetchConsentBannerInfo();
1953
+ store.getState().callbacks.onConsentSet?.({
1954
+ preferences: store.getState().consents
1955
+ });
1956
+ store.getState().fetchConsentBannerInfo();
1418
1957
  }
1419
1958
  return store;
1420
1959
  };
@@ -1422,4 +1961,4 @@ var __webpack_exports__deepMergeTranslations = __WEBPACK_EXTERNAL_MODULE__c15t_t
1422
1961
  var __webpack_exports__detectBrowserLanguage = __WEBPACK_EXTERNAL_MODULE__c15t_translations_cdae900b__.detectBrowserLanguage;
1423
1962
  var __webpack_exports__mergeTranslationConfigs = __WEBPACK_EXTERNAL_MODULE__c15t_translations_cdae900b__.mergeTranslationConfigs;
1424
1963
  var __webpack_exports__prepareTranslationConfig = __WEBPACK_EXTERNAL_MODULE__c15t_translations_cdae900b__.prepareTranslationConfig;
1425
- export { API_ENDPOINTS, C15tClient, CustomClient, OfflineClient, configureConsentManager, gdpr_consentTypes as consentTypes, createConsentManagerStore, createTrackingBlocker, defaultTranslationConfig, __webpack_exports__deepMergeTranslations as deepMergeTranslations, __webpack_exports__detectBrowserLanguage as detectBrowserLanguage, __webpack_exports__mergeTranslationConfigs as mergeTranslationConfigs, __webpack_exports__prepareTranslationConfig as prepareTranslationConfig };
1964
+ export { API_ENDPOINTS, C15tClient, CustomClient, OfflineClient, allConsentNames, configureConsentManager, gdpr_consentTypes as consentTypes, createConsentManagerStore, createIframeBlocker, createTrackingBlocker, defaultTranslationConfig, getLoadedScriptIds, isScriptLoaded, loadScripts, unloadScripts, core_updateScripts as updateScripts, __webpack_exports__deepMergeTranslations as deepMergeTranslations, __webpack_exports__detectBrowserLanguage as detectBrowserLanguage, __webpack_exports__mergeTranslationConfigs as mergeTranslationConfigs, __webpack_exports__prepareTranslationConfig as prepareTranslationConfig };