@osano/osano-cmp-react-native 0.9.0 → 0.10.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.
- package/lib/typescript/src/iab/gpp/api.d.ts +28 -0
- package/lib/typescript/src/iab/gpp/api.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/consent.d.ts +24 -0
- package/lib/typescript/src/iab/gpp/consent.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/constants.d.ts +37 -0
- package/lib/typescript/src/iab/gpp/constants.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/AbstractBase64UrlEncoder.d.ts +28 -0
- package/lib/typescript/src/iab/gpp/encoders/AbstractBase64UrlEncoder.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/BooleanEncoder.d.ts +34 -0
- package/lib/typescript/src/iab/gpp/encoders/BooleanEncoder.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/CompressedBase64UrlEncoder.d.ts +28 -0
- package/lib/typescript/src/iab/gpp/encoders/CompressedBase64UrlEncoder.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/DatetimeEncoder.d.ts +28 -0
- package/lib/typescript/src/iab/gpp/encoders/DatetimeEncoder.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/EncodableOptimizedFixedRange.d.ts +16 -0
- package/lib/typescript/src/iab/gpp/encoders/EncodableOptimizedFixedRange.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/FibonacciIntegerEncoder.d.ts +28 -0
- package/lib/typescript/src/iab/gpp/encoders/FibonacciIntegerEncoder.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/FibonacciIntegerRangeEncoder.d.ts +28 -0
- package/lib/typescript/src/iab/gpp/encoders/FibonacciIntegerRangeEncoder.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/FixedBitfieldEncoder.d.ts +28 -0
- package/lib/typescript/src/iab/gpp/encoders/FixedBitfieldEncoder.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/FixedIdBitMapEncoder.d.ts +6 -0
- package/lib/typescript/src/iab/gpp/encoders/FixedIdBitMapEncoder.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/FixedIntegerEncoder.d.ts +28 -0
- package/lib/typescript/src/iab/gpp/encoders/FixedIntegerEncoder.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/FixedIntegerListEncoder.d.ts +28 -0
- package/lib/typescript/src/iab/gpp/encoders/FixedIntegerListEncoder.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/FixedIntegerRangeEncoder.d.ts +28 -0
- package/lib/typescript/src/iab/gpp/encoders/FixedIntegerRangeEncoder.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/FixedStringEncoder.d.ts +26 -0
- package/lib/typescript/src/iab/gpp/encoders/FixedStringEncoder.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/TraditionalBase64UrlEncoder.d.ts +28 -0
- package/lib/typescript/src/iab/gpp/encoders/TraditionalBase64UrlEncoder.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/errors.d.ts +19 -0
- package/lib/typescript/src/iab/gpp/encoders/errors.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/index.d.ts +16 -0
- package/lib/typescript/src/iab/gpp/encoders/index.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/encoders/types.d.ts +5 -0
- package/lib/typescript/src/iab/gpp/encoders/types.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/index.d.ts +10 -0
- package/lib/typescript/src/iab/gpp/index.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/reducer.d.ts +6 -0
- package/lib/typescript/src/iab/gpp/reducer.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/selectors.d.ts +6 -0
- package/lib/typescript/src/iab/gpp/selectors.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/types.d.ts +3 -0
- package/lib/typescript/src/iab/gpp/types.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/v1/constants.d.ts +9 -0
- package/lib/typescript/src/iab/gpp/v1/constants.d.ts.map +1 -0
- package/lib/typescript/src/iab/gpp/v1/index.d.ts +19 -0
- package/lib/typescript/src/iab/gpp/v1/index.d.ts.map +1 -0
- package/lib/typescript/src/iab/usp/api.d.ts +4 -0
- package/lib/typescript/src/iab/usp/api.d.ts.map +1 -0
- package/lib/typescript/src/iab/usp/consent.d.ts +13 -0
- package/lib/typescript/src/iab/usp/consent.d.ts.map +1 -0
- package/lib/typescript/src/iab/usp/constants.d.ts +77 -0
- package/lib/typescript/src/iab/usp/constants.d.ts.map +1 -0
- package/lib/typescript/src/iab/usp/index.d.ts +10 -0
- package/lib/typescript/src/iab/usp/index.d.ts.map +1 -0
- package/lib/typescript/src/iab/usp/reducer.d.ts +13 -0
- package/lib/typescript/src/iab/usp/reducer.d.ts.map +1 -0
- package/lib/typescript/src/iab/usp/selectors.d.ts +9 -0
- package/lib/typescript/src/iab/usp/selectors.d.ts.map +1 -0
- package/lib/typescript/src/iab/usp/types.d.ts +5 -0
- package/lib/typescript/src/iab/usp/types.d.ts.map +1 -0
- package/lib/typescript/src/iab/usp/v1/constants.d.ts +9 -0
- package/lib/typescript/src/iab/usp/v1/constants.d.ts.map +1 -0
- package/lib/typescript/src/iab/usp/v1/index.d.ts +16 -0
- package/lib/typescript/src/iab/usp/v1/index.d.ts.map +1 -0
- package/package.json +2 -6
- package/src/iab/gpp/api.js +253 -0
- package/src/iab/gpp/consent.js +114 -0
- package/src/iab/gpp/constants.js +49 -0
- package/src/iab/gpp/encoders/AbstractBase64UrlEncoder.ts +89 -0
- package/src/iab/gpp/encoders/BooleanEncoder.ts +39 -0
- package/src/iab/gpp/encoders/CompressedBase64UrlEncoder.ts +41 -0
- package/src/iab/gpp/encoders/DatetimeEncoder.ts +46 -0
- package/src/iab/gpp/encoders/EncodableOptimizedFixedRange.ts +213 -0
- package/src/iab/gpp/encoders/FibonacciIntegerEncoder.ts +81 -0
- package/src/iab/gpp/encoders/FibonacciIntegerRangeEncoder.ts +122 -0
- package/src/iab/gpp/encoders/FixedBitfieldEncoder.ts +59 -0
- package/src/iab/gpp/encoders/FixedIdBitMapEncoder.ts +30 -0
- package/src/iab/gpp/encoders/FixedIntegerEncoder.ts +61 -0
- package/src/iab/gpp/encoders/FixedIntegerListEncoder.ts +87 -0
- package/src/iab/gpp/encoders/FixedIntegerRangeEncoder.ts +102 -0
- package/src/iab/gpp/encoders/FixedStringEncoder.ts +74 -0
- package/src/iab/gpp/encoders/LICENSE-IABTechLab +176 -0
- package/src/iab/gpp/encoders/TraditionalBase64UrlEncoder.ts +38 -0
- package/src/iab/gpp/encoders/errors.ts +41 -0
- package/src/iab/gpp/encoders/index.ts +15 -0
- package/src/iab/gpp/encoders/types.ts +5 -0
- package/src/iab/gpp/index.js +9 -0
- package/src/iab/gpp/reducer.js +7 -0
- package/src/iab/gpp/selectors.js +21 -0
- package/src/iab/gpp/types.ts +3 -0
- package/src/iab/gpp/v1/constants.js +7 -0
- package/src/iab/gpp/v1/index.js +73 -0
- package/src/iab/usp/api.js +56 -0
- package/src/iab/usp/consent.js +150 -0
- package/src/iab/usp/constants.js +40 -0
- package/src/iab/usp/index.js +9 -0
- package/src/iab/usp/reducer.js +107 -0
- package/src/iab/usp/selectors.js +31 -0
- package/src/iab/usp/types.ts +4 -0
- package/src/iab/usp/v1/constants.js +10 -0
- package/src/iab/usp/v1/index.js +54 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { SECTIONS } from './constants';
|
|
2
|
+
import { ScreenIds } from '../../constants';
|
|
3
|
+
import { get } from '../../utils';
|
|
4
|
+
|
|
5
|
+
export const isUiVisible = state =>
|
|
6
|
+
get(state, 'ui.currentScreen', ScreenIds.None) !== ScreenIds.None;
|
|
7
|
+
export const getSupportedApis = state =>
|
|
8
|
+
// We check state to filter out any unloaded section (e.g. if this is the
|
|
9
|
+
// non-IAB build then 'tcfeuv2' should not be returned because it's not
|
|
10
|
+
// available) and then use the SECTIONS constant to filter out any
|
|
11
|
+
// consentStrings that aren't in the spec.
|
|
12
|
+
Object.keys(get(state, 'iab.consentStrings', {})).filter(
|
|
13
|
+
section => !!SECTIONS[`${section}`],
|
|
14
|
+
);
|
|
15
|
+
export const doesGDPRApply = state =>
|
|
16
|
+
get(state, 'config.gdprApplies', false).toString() === 'true';
|
|
17
|
+
export const isBrowsingFrom = (state, jurisdiction) =>
|
|
18
|
+
get(state, 'config.jurisdiction', '').substring(0, jurisdiction.length) ===
|
|
19
|
+
jurisdiction;
|
|
20
|
+
export const getConsentString = (state, section) =>
|
|
21
|
+
get(state, `iab.consentStrings.${section}`, undefined);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
export const apiName = '__gpp';
|
|
2
|
+
// gppSection is the key in redux state that stores the consent string.
|
|
3
|
+
export const gppSection = 'gpp';
|
|
4
|
+
export const iframeName = '__gppLocator';
|
|
5
|
+
export const version = '1.0';
|
|
6
|
+
|
|
7
|
+
export const initOptions = { gppSection, version };
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import { apiName, gppSection, iframeName, initOptions } from './constants';
|
|
2
|
+
import { getConsentString, getSupportedApis, isUiVisible } from '../selectors';
|
|
3
|
+
|
|
4
|
+
import { types as ActionTypes } from '../../../redux/actions';
|
|
5
|
+
import { EventNames } from '../constants';
|
|
6
|
+
import makeApi from '../api';
|
|
7
|
+
import { makeFormatPingData } from '../consent';
|
|
8
|
+
import postMessageEventHandler from '../../iframe';
|
|
9
|
+
import { setConsentData } from '../../actions';
|
|
10
|
+
|
|
11
|
+
const formatPingData = makeFormatPingData(initOptions);
|
|
12
|
+
|
|
13
|
+
export function createMiddleware({ dispatchEvent } = {}) {
|
|
14
|
+
return store => next => action => {
|
|
15
|
+
const previousState = store.getState();
|
|
16
|
+
const result = next(action);
|
|
17
|
+
const { payload, type } = action;
|
|
18
|
+
const newState = store.getState();
|
|
19
|
+
const pingData = formatPingData(newState);
|
|
20
|
+
|
|
21
|
+
switch (type) {
|
|
22
|
+
case setConsentData.type: {
|
|
23
|
+
const sectionIsSupported = getSupportedApis(newState).includes(
|
|
24
|
+
payload.gppSection,
|
|
25
|
+
);
|
|
26
|
+
|
|
27
|
+
if (
|
|
28
|
+
dispatchEvent &&
|
|
29
|
+
sectionIsSupported &&
|
|
30
|
+
getConsentString(previousState, payload.gppSection) !== payload.value
|
|
31
|
+
) {
|
|
32
|
+
dispatchEvent({
|
|
33
|
+
eventName: EventNames.SECTION_CHANGED,
|
|
34
|
+
data: payload.gppSection,
|
|
35
|
+
pingData,
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
case ActionTypes.showDialog:
|
|
41
|
+
case ActionTypes.showDrawer:
|
|
42
|
+
case ActionTypes.showDoNotSell:
|
|
43
|
+
case ActionTypes.hideDialog:
|
|
44
|
+
case ActionTypes.hideDrawer:
|
|
45
|
+
case ActionTypes.hideDoNotSell:
|
|
46
|
+
case ActionTypes.hideAll: {
|
|
47
|
+
if (
|
|
48
|
+
dispatchEvent &&
|
|
49
|
+
isUiVisible(previousState) !== isUiVisible(newState)
|
|
50
|
+
) {
|
|
51
|
+
dispatchEvent({
|
|
52
|
+
eventName: EventNames.CMP_DISPLAY_STATUS_CHANGED,
|
|
53
|
+
data: pingData.cmpDisplayStatus,
|
|
54
|
+
pingData,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
break;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
return result;
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
const v1 = {
|
|
65
|
+
api: makeApi(initOptions),
|
|
66
|
+
apiName,
|
|
67
|
+
createMiddleware,
|
|
68
|
+
gppSection,
|
|
69
|
+
iframeName,
|
|
70
|
+
postMessageEventHandler: postMessageEventHandler(apiName),
|
|
71
|
+
};
|
|
72
|
+
|
|
73
|
+
export default v1;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { getConsentDataObject, makeGetField } from './consent';
|
|
2
|
+
|
|
3
|
+
export default function makeApi(initOptions) {
|
|
4
|
+
function bind(store) {
|
|
5
|
+
/**
|
|
6
|
+
* USPAPI implementation
|
|
7
|
+
* @function
|
|
8
|
+
* @name __uspapi
|
|
9
|
+
* @param {string} command - The command to be executed by the API
|
|
10
|
+
* @param {number} version - The version number that the response should adhere to
|
|
11
|
+
* @param {requestCallback} callback - The callback method that should be called with the command result
|
|
12
|
+
* @param {string} [parameter] - Optional parameter to be passed to the `command`
|
|
13
|
+
*/
|
|
14
|
+
return function uspApi(command, version, callback, parameter) {
|
|
15
|
+
const state = store.getState();
|
|
16
|
+
const getField = makeGetField(initOptions);
|
|
17
|
+
|
|
18
|
+
const getUSPData = (
|
|
19
|
+
providedState,
|
|
20
|
+
apiVersion = initOptions.apiVersion,
|
|
21
|
+
) => {
|
|
22
|
+
try {
|
|
23
|
+
const consentData = getConsentDataObject({
|
|
24
|
+
...initOptions,
|
|
25
|
+
version: apiVersion,
|
|
26
|
+
})(providedState);
|
|
27
|
+
return consentData;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
// Could not retrieve data
|
|
30
|
+
return undefined;
|
|
31
|
+
}
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
switch (command) {
|
|
35
|
+
case 'getUSPData': {
|
|
36
|
+
if (typeof callback === 'function') {
|
|
37
|
+
const uspData = getUSPData(state, version);
|
|
38
|
+
callback(uspData, !!uspData);
|
|
39
|
+
}
|
|
40
|
+
break;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
case 'getField': {
|
|
44
|
+
const field = parameter;
|
|
45
|
+
return getField(state, field);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
case 'getSection': {
|
|
49
|
+
return getUSPData(state, version);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
return { bind };
|
|
56
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { doesCCPAApply, getConsentString, getStore } from './selectors';
|
|
2
|
+
import { toBoolFromYesNoUn, toYesNoUnFromBool } from './constants';
|
|
3
|
+
|
|
4
|
+
import { merge } from '../../utils';
|
|
5
|
+
|
|
6
|
+
export const makeParseConsentString =
|
|
7
|
+
({ version: providedVersion = 1 } = {}) =>
|
|
8
|
+
uspString => {
|
|
9
|
+
const [version = providedVersion, notified, optOut, signatory] = (
|
|
10
|
+
uspString || ''
|
|
11
|
+
)
|
|
12
|
+
.split('')
|
|
13
|
+
.map((value, index) => {
|
|
14
|
+
if (index === 0) {
|
|
15
|
+
return isNaN(parseInt(value, 10))
|
|
16
|
+
? providedVersion
|
|
17
|
+
: parseInt(value, 10);
|
|
18
|
+
}
|
|
19
|
+
return toBoolFromYesNoUn(value);
|
|
20
|
+
});
|
|
21
|
+
return {
|
|
22
|
+
version,
|
|
23
|
+
notified,
|
|
24
|
+
signatory,
|
|
25
|
+
optOut,
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const makeGetField =
|
|
30
|
+
(initOptions = {}) =>
|
|
31
|
+
(state, field) => {
|
|
32
|
+
const [, notified, optOut, signatory] = getConsentString(
|
|
33
|
+
state,
|
|
34
|
+
initOptions,
|
|
35
|
+
).split('');
|
|
36
|
+
// These field names were found in the IAB GPP reference implementation
|
|
37
|
+
// README at
|
|
38
|
+
// https://github.com/IABTechLab/iabgpp-es/tree/42060d7ac2b6d0ee70fc1b5bcd33fc447bf5123c/modules/cmpapi
|
|
39
|
+
switch (field) {
|
|
40
|
+
case 'Version':
|
|
41
|
+
// This is always going to be 1 until the day this framework is
|
|
42
|
+
// removed so it's hardcoded
|
|
43
|
+
return 1;
|
|
44
|
+
case 'Notice':
|
|
45
|
+
return notified;
|
|
46
|
+
case 'OptOutSale':
|
|
47
|
+
return optOut;
|
|
48
|
+
case 'LspaCovered':
|
|
49
|
+
return signatory;
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* USPAPI formatConsentString implementation
|
|
55
|
+
* @function
|
|
56
|
+
* @param {Object} data
|
|
57
|
+
* @returns {string} Properly formatted USPData string
|
|
58
|
+
*/
|
|
59
|
+
export const makeFormatConsentString =
|
|
60
|
+
(initOptions = {}) =>
|
|
61
|
+
(data, state) => {
|
|
62
|
+
const {
|
|
63
|
+
version: storeVersion,
|
|
64
|
+
notified: storeNotified,
|
|
65
|
+
optOut: storeOptOut,
|
|
66
|
+
signatory: storeSignatory,
|
|
67
|
+
} = makeParseConsentString(initOptions)(
|
|
68
|
+
getConsentString(state, initOptions),
|
|
69
|
+
);
|
|
70
|
+
const {
|
|
71
|
+
version = storeVersion,
|
|
72
|
+
notified = storeNotified,
|
|
73
|
+
optOut = storeOptOut,
|
|
74
|
+
signatory = storeSignatory,
|
|
75
|
+
} = merge(getStore(state), data);
|
|
76
|
+
const consents = [notified, optOut, signatory];
|
|
77
|
+
/*
|
|
78
|
+
if (version !== storeVersion) {
|
|
79
|
+
// TODO: apply logic for future versions
|
|
80
|
+
}
|
|
81
|
+
*/
|
|
82
|
+
return doesCCPAApply(state)
|
|
83
|
+
? `${parseInt(version.toString(), 10)}${consents
|
|
84
|
+
.map(toYesNoUnFromBool)
|
|
85
|
+
.join('')}`
|
|
86
|
+
: `${parseInt(version.toString(), 10)}---`;
|
|
87
|
+
};
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* USPAPI consentFromOptions implementation
|
|
91
|
+
* @function
|
|
92
|
+
* @param {Object} data
|
|
93
|
+
* @returns {import('./constants').USPConsent} Properly formatted USPConsent object
|
|
94
|
+
*/
|
|
95
|
+
export const makeConsentFromOptions = initOptions => (data, state) => {
|
|
96
|
+
const {
|
|
97
|
+
version: storeVersion,
|
|
98
|
+
notified: storeNotified,
|
|
99
|
+
optOut: storeOptOut,
|
|
100
|
+
signatory: storeSignatory,
|
|
101
|
+
} = makeParseConsentString(initOptions)(getConsentString(state, initOptions));
|
|
102
|
+
/*
|
|
103
|
+
if (version !== storeVersion) {
|
|
104
|
+
// TODO: apply logic for future versions
|
|
105
|
+
}
|
|
106
|
+
*/
|
|
107
|
+
// We are going to set defaults if CCPA applies
|
|
108
|
+
const {
|
|
109
|
+
version = storeVersion,
|
|
110
|
+
notified = storeNotified,
|
|
111
|
+
optOut = storeOptOut,
|
|
112
|
+
signatory = storeSignatory,
|
|
113
|
+
} = merge(getStore(state), data);
|
|
114
|
+
return doesCCPAApply(state)
|
|
115
|
+
? {
|
|
116
|
+
version,
|
|
117
|
+
notified,
|
|
118
|
+
signatory,
|
|
119
|
+
optOut,
|
|
120
|
+
}
|
|
121
|
+
: { version };
|
|
122
|
+
};
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* USPAPI getConsentDataObject implementation
|
|
126
|
+
* @function
|
|
127
|
+
* @param {number} version
|
|
128
|
+
* @returns {import('./constants').USPData} Properly formatted USPData object
|
|
129
|
+
*/
|
|
130
|
+
export const getConsentDataObject = initOptions => state => {
|
|
131
|
+
const { version } = initOptions;
|
|
132
|
+
switch (version) {
|
|
133
|
+
// Supported versions
|
|
134
|
+
case 1: {
|
|
135
|
+
break;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
default: {
|
|
139
|
+
throw new Error('Unsupported version');
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
const consentString = makeFormatConsentString(initOptions)(
|
|
143
|
+
makeConsentFromOptions(initOptions)(undefined, state),
|
|
144
|
+
state,
|
|
145
|
+
);
|
|
146
|
+
return {
|
|
147
|
+
version,
|
|
148
|
+
uspString: consentString,
|
|
149
|
+
};
|
|
150
|
+
};
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* USPData object supplied to the __uspapi callback method
|
|
3
|
+
* @typedef {Object} USPData
|
|
4
|
+
* @property {number} version - indicates the U.S. Privacy spec version
|
|
5
|
+
* @property {(number|string)} uspString - Indicates the privacy application and agreement
|
|
6
|
+
* - String; agreement: “1YNN”, not applicable: “1---”
|
|
7
|
+
* - Number; 1 applies, 0 doesn’t apply, -1 not set
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* USPConsent object used for creating the USPData `uspString`
|
|
12
|
+
* @typedef {Object} USPConsent
|
|
13
|
+
* @property {number} version - indicates the U.S. Privacy spec version
|
|
14
|
+
* @property {(Boolean|undefined)} notified - Indicates that the end user has been notified of consent agreement
|
|
15
|
+
* @property {Boolean} optOut - Indicates that the end user has opted out of data resale
|
|
16
|
+
* @property {(Boolean|undefined)} signatory - Indicates that the provider has opted into the LSPA from IAB
|
|
17
|
+
*/
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* USPAPIPostMessageData object
|
|
21
|
+
* @typedef {Object} USPAPIPostMessageData
|
|
22
|
+
* @property {string} command - The command to be executed by the API
|
|
23
|
+
* @property {string} parameter - Optional parameter to be passed to the `command`
|
|
24
|
+
* @property {number} version - The version number that the response should adhere to
|
|
25
|
+
* @property {string} callId - The callback method that should be called with the command result
|
|
26
|
+
*/
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* USPAPIPostMessageEvent object supplied to the __uspapiLocator iframe postMessage handler
|
|
30
|
+
* @typedef {Object} USPAPIPostMessageEvent
|
|
31
|
+
* @property {(string|USPAPIPostMessageData)} data - The post message data as object or JSON string
|
|
32
|
+
* @property {string} origin - Indicates the origin of the window making the post
|
|
33
|
+
* @property {Window} source - The window which posted the message
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
export const toYesNoUnFromBool = b => (b === undefined ? '-' : b ? 'Y' : 'N');
|
|
37
|
+
export const toBoolFromYesNoUn = yn =>
|
|
38
|
+
yn === '-' || yn === undefined
|
|
39
|
+
? undefined
|
|
40
|
+
: !!((yn || 'N').toUpperCase() === 'Y');
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
import { ACCEPT, OPT_OUT, originalWindow } from '../../constants';
|
|
2
|
+
import { getSavedConsent, isGPCSupportEnabled } from '../../redux/selectors';
|
|
3
|
+
|
|
4
|
+
import { types as ActionTypes } from '../../redux/actions';
|
|
5
|
+
import { genericReducer } from '../../redux/helpers';
|
|
6
|
+
import { merge } from '../../utils';
|
|
7
|
+
|
|
8
|
+
export const initialState = {
|
|
9
|
+
iab: {
|
|
10
|
+
usp: {
|
|
11
|
+
ccpaApplies: false,
|
|
12
|
+
notified: undefined,
|
|
13
|
+
signatory: undefined,
|
|
14
|
+
optOut: false,
|
|
15
|
+
},
|
|
16
|
+
},
|
|
17
|
+
};
|
|
18
|
+
|
|
19
|
+
const updateOptions = (state, options) => {
|
|
20
|
+
const { config } = options || {};
|
|
21
|
+
// Country code is getting string replaced, so we pull it from the original state
|
|
22
|
+
const { config: { jurisdiction: oldJurisdiction } = {} } = state;
|
|
23
|
+
// Use a provided jurisdiction and default to the original
|
|
24
|
+
const { jurisdiction = oldJurisdiction } = config || {};
|
|
25
|
+
const {
|
|
26
|
+
ccpaApplies: providedCCPA = `${jurisdiction || ''}`
|
|
27
|
+
.toLowerCase()
|
|
28
|
+
.substring(0, 2) === 'us',
|
|
29
|
+
} = config || {};
|
|
30
|
+
|
|
31
|
+
const newState = merge(
|
|
32
|
+
{ ...state },
|
|
33
|
+
{
|
|
34
|
+
iab: {
|
|
35
|
+
usp: {
|
|
36
|
+
ccpaApplies: providedCCPA,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
);
|
|
41
|
+
|
|
42
|
+
return newState;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const formatOptOut = (state, acceptOrDeny) => {
|
|
46
|
+
return isGPCSupportEnabled(state)
|
|
47
|
+
? !!originalWindow.navigator.globalPrivacyControl
|
|
48
|
+
: acceptOrDeny === ACCEPT || acceptOrDeny === true;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
export default genericReducer(initialState, {
|
|
52
|
+
[ActionTypes.init]: updateOptions,
|
|
53
|
+
[ActionTypes.updateConfig]: (state, config) =>
|
|
54
|
+
updateOptions(state, { config }),
|
|
55
|
+
[ActionTypes.setConsent]: (state, { category, acceptOrDeny }) => {
|
|
56
|
+
if (typeof category === 'object' && category[`${OPT_OUT}`]) {
|
|
57
|
+
// Only populate the consent items we are using
|
|
58
|
+
return merge(
|
|
59
|
+
{ ...state },
|
|
60
|
+
{
|
|
61
|
+
iab: { usp: { optOut: formatOptOut(state, category[`${OPT_OUT}`]) } },
|
|
62
|
+
},
|
|
63
|
+
);
|
|
64
|
+
} else if (typeof category === 'string' && category === OPT_OUT) {
|
|
65
|
+
return merge(
|
|
66
|
+
{
|
|
67
|
+
...state,
|
|
68
|
+
},
|
|
69
|
+
{
|
|
70
|
+
iab: { usp: { optOut: formatOptOut(state, acceptOrDeny) } },
|
|
71
|
+
},
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
return state;
|
|
75
|
+
},
|
|
76
|
+
[ActionTypes.revertConsent]: state => {
|
|
77
|
+
const { [OPT_OUT]: optOut } = getSavedConsent(state) || {};
|
|
78
|
+
return merge(
|
|
79
|
+
{
|
|
80
|
+
...state,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
iab: { usp: { optOut: formatOptOut(state, optOut) } },
|
|
84
|
+
},
|
|
85
|
+
);
|
|
86
|
+
},
|
|
87
|
+
[ActionTypes.acceptAllConsent]: state =>
|
|
88
|
+
merge(
|
|
89
|
+
{
|
|
90
|
+
...state,
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
// Must be opposite because accepting is denying optOut
|
|
94
|
+
iab: { usp: { optOut: formatOptOut(state, false) } },
|
|
95
|
+
},
|
|
96
|
+
),
|
|
97
|
+
[ActionTypes.denyAllConsent]: state =>
|
|
98
|
+
merge(
|
|
99
|
+
{
|
|
100
|
+
...state,
|
|
101
|
+
},
|
|
102
|
+
{
|
|
103
|
+
// Must be opposite because denying is accepting optOut
|
|
104
|
+
iab: { usp: { optOut: formatOptOut(state, true) } },
|
|
105
|
+
},
|
|
106
|
+
),
|
|
107
|
+
});
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import { get } from '../../utils';
|
|
2
|
+
import { isBrowsingFromUS } from '../../redux/selectors';
|
|
3
|
+
import { toYesNoUnFromBool } from './constants';
|
|
4
|
+
|
|
5
|
+
export const DOMAIN = 'iab.usp';
|
|
6
|
+
|
|
7
|
+
export const getStore = state => get(state, DOMAIN, {});
|
|
8
|
+
export const doesCCPAApply = state =>
|
|
9
|
+
!!get(state, 'iab.usp.ccpaApplies', isBrowsingFromUS(state));
|
|
10
|
+
|
|
11
|
+
export const getConsentString = (
|
|
12
|
+
state,
|
|
13
|
+
{ gppSection, version: providedVersion } = {},
|
|
14
|
+
) => {
|
|
15
|
+
const savedString = get(state, ['iab', 'consentStrings', gppSection]);
|
|
16
|
+
if (savedString) {
|
|
17
|
+
return savedString;
|
|
18
|
+
}
|
|
19
|
+
const { notified, signatory, optOut } = getStore(state);
|
|
20
|
+
const consents = [notified, optOut, signatory];
|
|
21
|
+
const version = isNaN(parseInt(providedVersion, 10))
|
|
22
|
+
? 1
|
|
23
|
+
: parseInt(providedVersion, 10);
|
|
24
|
+
|
|
25
|
+
return doesCCPAApply(state)
|
|
26
|
+
? `${parseInt(version.toString(), 10)}${consents
|
|
27
|
+
.map(toYesNoUnFromBool)
|
|
28
|
+
.join('')}`
|
|
29
|
+
: `${parseInt(version.toString(), 10)}---`;
|
|
30
|
+
};
|
|
31
|
+
export const getOptOut = state => getStore(state).optOut;
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export const apiName = '__uspapi';
|
|
2
|
+
// gppSection is the key in redux state that stores the consent string.
|
|
3
|
+
export const gppSection = 'uspv1';
|
|
4
|
+
export const iframeName = '__uspapiLocator';
|
|
5
|
+
export const version = 1;
|
|
6
|
+
|
|
7
|
+
export const initOptions = {
|
|
8
|
+
gppSection,
|
|
9
|
+
version,
|
|
10
|
+
};
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
import { ACCEPT, DENY, OPT_OUT } from '../../../constants';
|
|
2
|
+
import { apiName, gppSection, iframeName, initOptions } from './constants';
|
|
3
|
+
import { makeConsentFromOptions, makeFormatConsentString } from '../consent';
|
|
4
|
+
|
|
5
|
+
import { types as ActionTypes } from '../../../redux/actions';
|
|
6
|
+
import { getConsent } from '../../../redux/selectors';
|
|
7
|
+
import makeApi from '../api';
|
|
8
|
+
import postMessageEventHandler from '../../iframe';
|
|
9
|
+
import { setConsentData } from '../../actions';
|
|
10
|
+
|
|
11
|
+
const consentFromOptions = makeConsentFromOptions(initOptions);
|
|
12
|
+
const formatConsentString = makeFormatConsentString(initOptions);
|
|
13
|
+
|
|
14
|
+
function createMiddleware() {
|
|
15
|
+
return store => next => action => {
|
|
16
|
+
const result = next(action);
|
|
17
|
+
const { type } = action;
|
|
18
|
+
const state = store.getState();
|
|
19
|
+
switch (type) {
|
|
20
|
+
case ActionTypes.saveConsent: {
|
|
21
|
+
const { [OPT_OUT]: optOut } = getConsent(state);
|
|
22
|
+
const consentString = formatConsentString(
|
|
23
|
+
consentFromOptions(
|
|
24
|
+
{ optOut: optOut === ACCEPT ? true : false },
|
|
25
|
+
state,
|
|
26
|
+
),
|
|
27
|
+
state,
|
|
28
|
+
);
|
|
29
|
+
store.dispatch(setConsentData(gppSection, consentString));
|
|
30
|
+
break;
|
|
31
|
+
}
|
|
32
|
+
case ActionTypes.clearConsent: {
|
|
33
|
+
const consentString = formatConsentString(
|
|
34
|
+
consentFromOptions({ optOut: DENY }, state),
|
|
35
|
+
state,
|
|
36
|
+
);
|
|
37
|
+
store.dispatch(setConsentData(gppSection, consentString));
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
return result;
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const v1 = {
|
|
46
|
+
api: makeApi(initOptions),
|
|
47
|
+
apiName,
|
|
48
|
+
createMiddleware,
|
|
49
|
+
gppSection,
|
|
50
|
+
iframeName,
|
|
51
|
+
postMessageEventHandler: postMessageEventHandler(apiName, true),
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
export default v1;
|