@adobe/alloy 2.14.0 → 2.15.0-alpha.1

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 (35) hide show
  1. package/libEs5/components/ActivityCollector/attachClickActivityCollector.js +1 -1
  2. package/libEs5/components/ActivityCollector/configValidators.js +2 -1
  3. package/libEs5/components/ActivityCollector/createGetLinkDetails.js +79 -0
  4. package/libEs5/components/ActivityCollector/createLinkClick.js +22 -64
  5. package/libEs5/components/ActivityCollector/getLinkName.js +158 -0
  6. package/libEs5/components/ActivityCollector/getLinkRegion.js +76 -0
  7. package/libEs5/components/ActivityCollector/index.js +43 -4
  8. package/libEs5/components/ActivityCollector/utils.js +52 -2
  9. package/libEs5/components/Audiences/injectProcessDestinations.js +1 -3
  10. package/libEs5/components/Personalization/createClickStorage.js +4 -2
  11. package/libEs5/components/Personalization/createExecuteDecisions.js +8 -2
  12. package/libEs5/components/Personalization/createOnClickHandler.js +4 -2
  13. package/libEs5/components/Personalization/dom-actions/clicks/collectClicks.js +57 -6
  14. package/libEs5/components/Personalization/event.js +12 -2
  15. package/libEs5/constants/libraryVersion.js +1 -1
  16. package/libEs5/core/buildAndValidateConfig.js +33 -21
  17. package/libEs5/core/componentCreators.js +4 -0
  18. package/libEs6/components/ActivityCollector/attachClickActivityCollector.js +1 -1
  19. package/libEs6/components/ActivityCollector/configValidators.js +3 -2
  20. package/libEs6/components/ActivityCollector/createGetLinkDetails.js +77 -0
  21. package/libEs6/components/ActivityCollector/createLinkClick.js +24 -64
  22. package/libEs6/components/ActivityCollector/getLinkName.js +151 -0
  23. package/libEs6/components/ActivityCollector/getLinkRegion.js +68 -0
  24. package/libEs6/components/ActivityCollector/index.js +39 -4
  25. package/libEs6/components/ActivityCollector/utils.js +43 -1
  26. package/libEs6/components/Audiences/injectProcessDestinations.js +1 -3
  27. package/libEs6/components/Personalization/createClickStorage.js +4 -2
  28. package/libEs6/components/Personalization/createExecuteDecisions.js +6 -2
  29. package/libEs6/components/Personalization/createOnClickHandler.js +5 -2
  30. package/libEs6/components/Personalization/dom-actions/clicks/collectClicks.js +50 -6
  31. package/libEs6/components/Personalization/event.js +13 -3
  32. package/libEs6/constants/libraryVersion.js +1 -1
  33. package/libEs6/core/buildAndValidateConfig.js +19 -2
  34. package/libEs6/core/componentCreators.js +4 -0
  35. package/package.json +3 -3
@@ -12,13 +12,30 @@ governing permissions and limitations under the License.
12
12
  import attachClickActivityCollector from "./attachClickActivityCollector";
13
13
  import configValidators from "./configValidators";
14
14
  import createLinkClick from "./createLinkClick";
15
+ import createGetLinkDetails from "./createGetLinkDetails";
16
+ import getLinkName from "./getLinkName";
17
+ import getLinkRegion from "./getLinkRegion";
18
+ import { determineLinkType, findSupportedAnchorElement, getAbsoluteUrlFromAnchorElement } from "./utils";
19
+ const getLinkDetails = createGetLinkDetails({
20
+ window,
21
+ getLinkName,
22
+ getLinkRegion,
23
+ getAbsoluteUrlFromAnchorElement,
24
+ findSupportedAnchorElement,
25
+ determineLinkType
26
+ });
15
27
 
16
28
  const createActivityCollector = ({
17
29
  config,
18
30
  eventManager,
19
- handleError
31
+ handleError,
32
+ logger
20
33
  }) => {
21
- const linkClick = createLinkClick(window, config);
34
+ const linkClick = createLinkClick({
35
+ getLinkDetails,
36
+ config,
37
+ logger
38
+ });
22
39
  return {
23
40
  lifecycle: {
24
41
  onComponentsRegistered(tools) {
@@ -26,7 +43,6 @@ const createActivityCollector = ({
26
43
  lifecycle
27
44
  } = tools;
28
45
  attachClickActivityCollector({
29
- config,
30
46
  eventManager,
31
47
  lifecycle,
32
48
  handleError
@@ -37,7 +53,10 @@ const createActivityCollector = ({
37
53
  event,
38
54
  clickedElement
39
55
  }) {
40
- linkClick(event, clickedElement);
56
+ linkClick({
57
+ targetElement: clickedElement,
58
+ event
59
+ });
41
60
  }
42
61
 
43
62
  }
@@ -46,4 +65,20 @@ const createActivityCollector = ({
46
65
 
47
66
  createActivityCollector.namespace = "ActivityCollector";
48
67
  createActivityCollector.configValidators = configValidators;
68
+
69
+ createActivityCollector.buildOnInstanceConfiguredExtraParams = ({
70
+ config,
71
+ logger
72
+ }) => {
73
+ return {
74
+ getLinkDetails: targetElement => {
75
+ return getLinkDetails({
76
+ targetElement,
77
+ config,
78
+ logger
79
+ });
80
+ }
81
+ };
82
+ };
83
+
49
84
  export default createActivityCollector;
@@ -68,5 +68,47 @@ const isExitLink = (window, linkUrl) => {
68
68
 
69
69
  return true;
70
70
  };
71
+ /**
72
+ * Reduces repeated whitespace within a string. Whitespace surrounding the string
73
+ * is trimmed and any occurrence of whitespace within the string is replaced with
74
+ * a single space.
75
+ * @param {string} str String to be formatted.
76
+ * @returns {string} Formatted string.
77
+ */
71
78
 
72
- export { urlStartsWithScheme, getAbsoluteUrlFromAnchorElement, isSupportedAnchorElement, isDownloadLink, isExitLink };
79
+
80
+ const truncateWhiteSpace = str => {
81
+ return str && str.replace(/\s+/g, " ").trim();
82
+ };
83
+
84
+ const isEmptyString = str => {
85
+ return !str || str.length === 0;
86
+ };
87
+
88
+ const determineLinkType = (window, config, linkUrl, clickedObj) => {
89
+ let linkType = "other";
90
+
91
+ if (isDownloadLink(config.downloadLinkQualifier, linkUrl, clickedObj)) {
92
+ linkType = "download";
93
+ } else if (isExitLink(window, linkUrl)) {
94
+ linkType = "exit";
95
+ }
96
+
97
+ return linkType;
98
+ };
99
+
100
+ const findSupportedAnchorElement = targetElement => {
101
+ let node = targetElement;
102
+
103
+ while (node) {
104
+ if (isSupportedAnchorElement(node)) {
105
+ return node;
106
+ }
107
+
108
+ node = node.parentNode;
109
+ }
110
+
111
+ return null;
112
+ };
113
+
114
+ export { urlStartsWithScheme, getAbsoluteUrlFromAnchorElement, isSupportedAnchorElement, isDownloadLink, isEmptyString, isExitLink, truncateWhiteSpace, findSupportedAnchorElement, determineLinkType };
@@ -49,11 +49,9 @@ export default (({
49
49
  return Promise.all(urlDestinations.map(urlDestination => {
50
50
  return fireReferrerHideableImage(urlDestination.spec).then(() => {
51
51
  logger.info(createResultLogMessage(urlDestination, true));
52
- }).catch(() => {
53
- // We intentionally do not throw an error if destinations fail. We
52
+ }).catch(() => {// We intentionally do not throw an error if destinations fail. We
54
53
  // consider it a non-critical failure and therefore do not want it to
55
54
  // reject the promise handed back to the customer.
56
- logger.error(createResultLogMessage(urlDestination, false));
57
55
  });
58
56
  })).then(noop);
59
57
  };
@@ -14,7 +14,8 @@ const metasToArray = metas => {
14
14
  return {
15
15
  id: key,
16
16
  scope: metas[key].scope,
17
- scopeDetails: metas[key].scopeDetails
17
+ scopeDetails: metas[key].scopeDetails,
18
+ trackingLabel: metas[key].trackingLabel
18
19
  };
19
20
  });
20
21
  };
@@ -29,7 +30,8 @@ export default (() => {
29
30
 
30
31
  clickStorage[value.selector][value.meta.id] = {
31
32
  scope: value.meta.scope,
32
- scopeDetails: value.meta.scopeDetails
33
+ scopeDetails: value.meta.scopeDetails,
34
+ trackingLabel: value.meta.trackingLabel
33
35
  };
34
36
  };
35
37
 
@@ -14,8 +14,12 @@ const DEFAULT_ACTION_TYPE = "defaultContent";
14
14
 
15
15
  const identity = item => item;
16
16
 
17
+ const getItemMeta = (item, decisionMeta) => item.characteristics && item.characteristics.trackingLabel ? assign({
18
+ trackingLabel: item.characteristics.trackingLabel
19
+ }, decisionMeta) : decisionMeta;
20
+
17
21
  const buildActions = decision => {
18
- const meta = {
22
+ const decisionMeta = {
19
23
  id: decision.id,
20
24
  scope: decision.scope,
21
25
  scopeDetails: decision.scopeDetails
@@ -23,7 +27,7 @@ const buildActions = decision => {
23
27
  return decision.items.map(item => assign({
24
28
  type: DEFAULT_ACTION_TYPE
25
29
  }, item.data, {
26
- meta
30
+ meta: getItemMeta(item, decisionMeta)
27
31
  }));
28
32
  };
29
33
 
@@ -27,7 +27,10 @@ export default (({
27
27
  const selectors = getClickSelectors();
28
28
 
29
29
  if (isNonEmptyArray(selectors)) {
30
- const decisionsMeta = collectClicks(clickedElement, selectors, getClickMetasBySelector);
30
+ const {
31
+ decisionsMeta,
32
+ eventLabel
33
+ } = collectClicks(clickedElement, selectors, getClickMetasBySelector);
31
34
 
32
35
  if (isNonEmptyArray(decisionsMeta)) {
33
36
  const xdm = {
@@ -44,7 +47,7 @@ export default (({
44
47
  }
45
48
 
46
49
  event.mergeXdm(xdm);
47
- mergeDecisionsMeta(event, decisionsMeta, PropositionEventType.INTERACT);
50
+ mergeDecisionsMeta(event, decisionsMeta, PropositionEventType.INTERACT, eventLabel);
48
51
  }
49
52
  }
50
53
  };
@@ -16,28 +16,72 @@ const getMetasIfMatches = (clickedElement, selector, getClickMetasBySelector) =>
16
16
  documentElement
17
17
  } = document;
18
18
  let element = clickedElement;
19
+ let i = 0;
19
20
 
20
21
  while (element && element !== documentElement) {
21
22
  if (matchesSelectorWithEq(selector, element)) {
22
- return getClickMetasBySelector(selector);
23
+ const matchedMetas = getClickMetasBySelector(selector);
24
+ const foundMetaWithLabel = matchedMetas.find(meta => meta.trackingLabel);
25
+
26
+ if (foundMetaWithLabel) {
27
+ return {
28
+ metas: matchedMetas,
29
+ label: foundMetaWithLabel.trackingLabel,
30
+ weight: i
31
+ };
32
+ }
33
+
34
+ return {
35
+ metas: matchedMetas
36
+ };
23
37
  }
24
38
 
25
39
  element = element.parentNode;
40
+ i += 1;
26
41
  }
27
42
 
28
- return null;
43
+ return {
44
+ metas: null
45
+ };
29
46
  };
30
47
 
48
+ const cleanMetas = metas => metas.map(meta => {
49
+ delete meta.trackingLabel;
50
+ return meta;
51
+ });
52
+
53
+ const dedupMetas = metas => metas.filter((meta, index) => {
54
+ const stringifiedMeta = JSON.stringify(meta);
55
+ return index === metas.findIndex(innerMeta => JSON.stringify(innerMeta) === stringifiedMeta);
56
+ });
57
+
31
58
  export default ((clickedElement, selectors, getClickMetasBySelector) => {
32
59
  const result = [];
60
+ let resultLabel = "";
61
+ let resultLabelWeight = Number.MAX_SAFE_INTEGER;
62
+ /* eslint-disable no-continue */
33
63
 
34
64
  for (let i = 0; i < selectors.length; i += 1) {
35
- const metas = getMetasIfMatches(clickedElement, selectors[i], getClickMetasBySelector);
65
+ const {
66
+ metas,
67
+ label,
68
+ weight
69
+ } = getMetasIfMatches(clickedElement, selectors[i], getClickMetasBySelector);
36
70
 
37
- if (metas) {
38
- result.push(...metas);
71
+ if (!metas) {
72
+ continue;
39
73
  }
74
+
75
+ if (label && weight <= resultLabelWeight) {
76
+ resultLabel = label;
77
+ resultLabelWeight = weight;
78
+ }
79
+
80
+ result.push(...cleanMetas(metas));
40
81
  }
41
82
 
42
- return result;
83
+ return {
84
+ decisionsMeta: dedupMetas(result),
85
+ eventLabel: resultLabel
86
+ };
43
87
  });
@@ -10,8 +10,10 @@ OF ANY KIND, either express or implied. See the License for the specific languag
10
10
  governing permissions and limitations under the License.
11
11
  */
12
12
  const EVENT_TYPE_TRUE = 1;
13
- export const mergeDecisionsMeta = (event, decisionsMeta, eventType) => {
14
- event.mergeXdm({
13
+ /* eslint-disable no-underscore-dangle */
14
+
15
+ export const mergeDecisionsMeta = (event, decisionsMeta, eventType, eventLabel = "") => {
16
+ const xdm = {
15
17
  _experience: {
16
18
  decisioning: {
17
19
  propositions: decisionsMeta,
@@ -20,7 +22,15 @@ export const mergeDecisionsMeta = (event, decisionsMeta, eventType) => {
20
22
  }
21
23
  }
22
24
  }
23
- });
25
+ };
26
+
27
+ if (eventLabel) {
28
+ xdm._experience.decisioning.propositionAction = {
29
+ label: eventLabel
30
+ };
31
+ }
32
+
33
+ event.mergeXdm(xdm);
24
34
  };
25
35
  export const mergeQuery = (event, details) => {
26
36
  event.mergeQuery({
@@ -11,4 +11,4 @@ governing permissions and limitations under the License.
11
11
  */
12
12
  // The __VERSION__ keyword will be replace at alloy build time with the package.json version.
13
13
  // see babel-plugin-version
14
- export default "2.14.0";
14
+ export default "2.15.0-alpha.1";
@@ -34,6 +34,21 @@ const transformOptions = (schema, options) => {
34
34
  }
35
35
  };
36
36
 
37
+ const buildAllOnInstanceConfiguredExtraParams = (config, logger, componentCreators) => {
38
+ return componentCreators.reduce((memo, {
39
+ buildOnInstanceConfiguredExtraParams
40
+ }) => {
41
+ if (buildOnInstanceConfiguredExtraParams) {
42
+ assign(memo, buildOnInstanceConfiguredExtraParams({
43
+ config,
44
+ logger
45
+ }));
46
+ }
47
+
48
+ return memo;
49
+ }, {});
50
+ };
51
+
37
52
  export default (({
38
53
  options,
39
54
  componentCreators,
@@ -46,8 +61,10 @@ export default (({
46
61
  const config = createConfig(transformOptions(schema, options));
47
62
  setDebugEnabled(config.debugEnabled, {
48
63
  fromConfig: true
49
- });
50
- logger.logOnInstanceConfigured({
64
+ }); // eslint-disable-next-line no-underscore-dangle
65
+
66
+ const extraParams = buildAllOnInstanceConfiguredExtraParams(config, logger, componentCreators);
67
+ logger.logOnInstanceConfigured({ ...extraParams,
51
68
  config
52
69
  });
53
70
  return config;
@@ -9,6 +9,10 @@ the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR REPRESENTA
9
9
  OF ANY KIND, either express or implied. See the License for the specific language
10
10
  governing permissions and limitations under the License.
11
11
  */
12
+ // This is the only place where core is allowed to import from components.
13
+ // This makes sure that each component could be removed without breaking the library
14
+
15
+ /* eslint-disable import/no-restricted-paths */
12
16
  import createDataCollector from "../components/DataCollector";
13
17
  import createActivityCollector from "../components/ActivityCollector";
14
18
  import createIdentity from "../components/Identity";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@adobe/alloy",
3
- "version": "2.14.0",
3
+ "version": "2.15.0-alpha.1",
4
4
  "description": "Adobe Experience Platform Web SDK",
5
5
  "main": "libEs5/index.js",
6
6
  "module": "libEs6/index.js",
@@ -64,7 +64,7 @@
64
64
  "uuid": "^3.3.2"
65
65
  },
66
66
  "devDependencies": {
67
- "@adobe/alloy": "^2.14.0-beta.1",
67
+ "@adobe/alloy": "^2.15.0-alpha.0",
68
68
  "@babel/cli": "^7.12.8",
69
69
  "@babel/core": "^7.2.2",
70
70
  "@babel/plugin-proposal-object-rest-spread": "^7.3.2",
@@ -118,7 +118,7 @@
118
118
  "rollup-plugin-terser": "^7.0.2",
119
119
  "semver": "^7.3.7",
120
120
  "start-server-and-test": "^1.10.6",
121
- "testcafe": "^2.2.0",
121
+ "testcafe": "^2.3.1",
122
122
  "testcafe-browser-provider-saucelabs": "^1.9.0",
123
123
  "testcafe-reporter-junit": "^3.0.2",
124
124
  "url-exists-nodejs": "^0.1.0",