@microsoft/applicationinsights-clickanalytics-js 2.7.5-nightly.2203-03 → 2.7.5
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/browser/ai.clck.2.7.5.cjs.js +2011 -0
- package/browser/ai.clck.2.7.5.cjs.js.map +1 -0
- package/browser/ai.clck.2.7.5.cjs.min.js +6 -0
- package/browser/ai.clck.2.7.5.cjs.min.js.map +1 -0
- package/browser/ai.clck.2.7.5.gbl.js +2015 -0
- package/browser/ai.clck.2.7.5.gbl.js.map +1 -0
- package/browser/ai.clck.2.7.5.gbl.min.js +6 -0
- package/browser/ai.clck.2.7.5.gbl.min.js.map +1 -0
- package/browser/ai.clck.2.7.5.integrity.json +66 -0
- package/browser/ai.clck.2.7.5.js +2017 -0
- package/browser/ai.clck.2.7.5.js.map +1 -0
- package/browser/ai.clck.2.7.5.min.js +6 -0
- package/browser/ai.clck.2.7.5.min.js.map +1 -0
- package/browser/ai.clck.2.cjs.js +810 -1682
- package/browser/ai.clck.2.cjs.js.map +1 -1
- package/browser/ai.clck.2.cjs.min.js +2 -2
- package/browser/ai.clck.2.cjs.min.js.map +1 -1
- package/browser/ai.clck.2.gbl.js +810 -1682
- package/browser/ai.clck.2.gbl.js.map +1 -1
- package/browser/ai.clck.2.gbl.min.js +2 -2
- package/browser/ai.clck.2.gbl.min.js.map +1 -1
- package/browser/ai.clck.2.js +810 -1682
- package/browser/ai.clck.2.js.map +1 -1
- package/browser/ai.clck.2.min.js +2 -2
- package/browser/ai.clck.2.min.js.map +1 -1
- package/dist/applicationinsights-clickanalytics-js.api.json +27 -29
- package/dist/applicationinsights-clickanalytics-js.api.md +0 -1
- package/dist/applicationinsights-clickanalytics-js.d.ts +5 -2
- package/dist/applicationinsights-clickanalytics-js.js +810 -1682
- package/dist/applicationinsights-clickanalytics-js.js.map +1 -1
- package/dist/applicationinsights-clickanalytics-js.min.js +2 -2
- package/dist/applicationinsights-clickanalytics-js.min.js.map +1 -1
- package/dist/applicationinsights-clickanalytics-js.rollup.d.ts +5 -2
- package/dist-esm/Behaviours.js +1 -1
- package/dist-esm/ClickAnalyticsPlugin.js +50 -74
- package/dist-esm/ClickAnalyticsPlugin.js.map +1 -1
- package/dist-esm/DataCollector.js +2 -4
- package/dist-esm/DataCollector.js.map +1 -1
- package/dist-esm/Enums.js +1 -1
- package/dist-esm/Interfaces/Datamodel.js +1 -1
- package/dist-esm/applicationinsights-clickanalytics-js.js +1 -1
- package/dist-esm/common/Utils.js +1 -1
- package/dist-esm/events/PageAction.js +126 -128
- package/dist-esm/events/PageAction.js.map +1 -1
- package/dist-esm/events/WebEvent.js +94 -109
- package/dist-esm/events/WebEvent.js.map +1 -1
- package/dist-esm/handlers/AutoCaptureHandler.js +83 -85
- package/dist-esm/handlers/AutoCaptureHandler.js.map +1 -1
- package/dist-esm/handlers/DomContentHandler.js +276 -279
- package/dist-esm/handlers/DomContentHandler.js.map +1 -1
- package/package.json +56 -59
- package/src/ClickAnalyticsPlugin.ts +47 -92
- package/src/DataCollector.ts +22 -24
- package/src/Interfaces/Datamodel.ts +21 -20
- package/src/common/Utils.ts +8 -8
- package/src/events/PageAction.ts +131 -165
- package/src/events/WebEvent.ts +78 -147
- package/src/handlers/AutoCaptureHandler.ts +79 -89
- package/src/handlers/DomContentHandler.ts +303 -336
- package/types/ClickAnalyticsPlugin.d.ts +4 -1
- package/types/Interfaces/Datamodel.d.ts +2 -3
- package/types/events/PageAction.d.ts +6 -6
- package/types/events/WebEvent.d.ts +17 -17
- package/types/handlers/AutoCaptureHandler.d.ts +10 -2
- package/types/handlers/DomContentHandler.d.ts +50 -3
- package/types/tsdoc-metadata.json +1 -1
- package/browser/ai.clck.2.7.5-nightly.2203-03.cjs.js +0 -2883
- package/browser/ai.clck.2.7.5-nightly.2203-03.cjs.js.map +0 -1
- package/browser/ai.clck.2.7.5-nightly.2203-03.cjs.min.js +0 -6
- package/browser/ai.clck.2.7.5-nightly.2203-03.cjs.min.js.map +0 -1
- package/browser/ai.clck.2.7.5-nightly.2203-03.gbl.js +0 -2887
- package/browser/ai.clck.2.7.5-nightly.2203-03.gbl.js.map +0 -1
- package/browser/ai.clck.2.7.5-nightly.2203-03.gbl.min.js +0 -6
- package/browser/ai.clck.2.7.5-nightly.2203-03.gbl.min.js.map +0 -1
- package/browser/ai.clck.2.7.5-nightly.2203-03.integrity.json +0 -66
- package/browser/ai.clck.2.7.5-nightly.2203-03.js +0 -2889
- package/browser/ai.clck.2.7.5-nightly.2203-03.js.map +0 -1
- package/browser/ai.clck.2.7.5-nightly.2203-03.min.js +0 -6
- package/browser/ai.clck.2.7.5-nightly.2203-03.min.js.map +0 -1
|
@@ -1,14 +1,12 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @copyright Microsoft 2020
|
|
3
3
|
*/
|
|
4
|
-
|
|
5
|
-
import dynamicProto from "@microsoft/dynamicproto-js";
|
|
6
4
|
import {
|
|
7
5
|
removeInvalidElements,
|
|
8
6
|
walkUpDomChainWithElementValidation,
|
|
9
7
|
extend, _ExtendedInternalMessageId, isValueAssigned
|
|
10
8
|
} from "../common/Utils";
|
|
11
|
-
import { IDiagnosticLogger, LoggingSeverity, getDocument, isNullOrUndefined, hasDocument
|
|
9
|
+
import { IDiagnosticLogger, LoggingSeverity, getDocument, isNullOrUndefined, hasDocument} from "@microsoft/applicationinsights-core-js";
|
|
12
10
|
import { IClickAnalyticsConfiguration, IContent, IContentHandler } from "../Interfaces/Datamodel";
|
|
13
11
|
|
|
14
12
|
const MAX_CONTENTNAME_LENGTH = 200;
|
|
@@ -20,378 +18,347 @@ export class DomContentHandler implements IContentHandler {
|
|
|
20
18
|
* @param traceLogger - Trace logger to log to console.
|
|
21
19
|
*/
|
|
22
20
|
constructor(protected _config: IClickAnalyticsConfiguration, protected _traceLogger: IDiagnosticLogger) {
|
|
21
|
+
}
|
|
23
22
|
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
metaTags = isValueAssigned(dataTags.metaDataPrefix) ? _getMetaDataFromDOM(dataTags.captureAllMetaDataContent, dataTags.metaDataPrefix, false) :
|
|
31
|
-
_getMetaDataFromDOM(dataTags.captureAllMetaDataContent ,"", false);
|
|
32
|
-
}
|
|
33
|
-
return metaTags;
|
|
34
|
-
};
|
|
35
|
-
|
|
36
|
-
_self.getElementContent = (element: Element): IContent => {
|
|
37
|
-
|
|
38
|
-
if (!element) {
|
|
39
|
-
return {};
|
|
40
|
-
}
|
|
23
|
+
/**
|
|
24
|
+
* Collect metatags from DOM.
|
|
25
|
+
* Collect data from meta tags.
|
|
26
|
+
* @returns {object} - Metatags collection/property bag
|
|
27
|
+
*/
|
|
28
|
+
public getMetadata(): { [name: string]: string } {
|
|
41
29
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
parentDataTagPrefix = dataTagPrefix + dataTags.parentDataTag;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
if (!_isTracked(element, dataTagPrefix, aiBlobAttributeTag)) {
|
|
53
|
-
// capture blob from element or hierarchy
|
|
54
|
-
biBlobValue = element.getAttribute(aiBlobAttributeTag);
|
|
55
|
-
if (biBlobValue) {
|
|
56
|
-
try {
|
|
57
|
-
elementContent = JSON.parse(biBlobValue);
|
|
58
|
-
} catch (e) {
|
|
59
|
-
_self._traceLogger.throwInternal(
|
|
60
|
-
LoggingSeverity.CRITICAL,
|
|
61
|
-
_ExtendedInternalMessageId.CannotParseAiBlobValue, "Can not parse " + biBlobValue
|
|
62
|
-
);
|
|
63
|
-
}
|
|
64
|
-
} else {
|
|
65
|
-
// traverse up the DOM to find the closest parent with data-* tag defined
|
|
66
|
-
//contentElement = walkUpDomChainWithElementValidation(element, _self._isTracked, dataTagPrefix);
|
|
67
|
-
elementContent = extend(elementContent, _populateElementContent(element, dataTagPrefix, parentDataTagPrefix, aiBlobAttributeTag));
|
|
68
|
-
}
|
|
69
|
-
} else {
|
|
70
|
-
elementContent = extend(elementContent, _populateElementContentwithDataTag(element, dataTagPrefix, parentDataTagPrefix, aiBlobAttributeTag));
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
removeInvalidElements(elementContent);
|
|
30
|
+
let metaTags = {};
|
|
31
|
+
if (hasDocument) {
|
|
32
|
+
metaTags = isValueAssigned(this._config.dataTags.metaDataPrefix) ? this._getMetaDataFromDOM(this._config.dataTags.captureAllMetaDataContent ,this._config.dataTags.metaDataPrefix, false) :
|
|
33
|
+
this._getMetaDataFromDOM(this._config.dataTags.captureAllMetaDataContent ,"", false);
|
|
34
|
+
}
|
|
35
|
+
return metaTags;
|
|
36
|
+
}
|
|
74
37
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
38
|
+
/**
|
|
39
|
+
* Collect data-* attributes for the given element.
|
|
40
|
+
* All attributes with data-* prefix or user provided customDataPrefix are collected.'data-*' prefix is removed from the key name.
|
|
41
|
+
* @param element - The element from which attributes need to be collected.
|
|
42
|
+
* @returns String representation of the Json array of element attributes
|
|
43
|
+
*/
|
|
44
|
+
public getElementContent(element: Element): IContent {
|
|
78
45
|
|
|
79
|
-
|
|
80
|
-
};
|
|
46
|
+
if (!element) {
|
|
47
|
+
return {};
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
let elementContent: any = {};
|
|
51
|
+
let biBlobValue;
|
|
52
|
+
let parentDataTagPrefix;
|
|
53
|
+
const dataTagPrefix:string = this._config.dataTags.customDataPrefix;
|
|
54
|
+
const aiBlobAttributeTag:string = dataTagPrefix + this._config.dataTags.aiBlobAttributeTag;
|
|
55
|
+
if(isValueAssigned(this._config.dataTags.parentDataTag)) {
|
|
56
|
+
parentDataTagPrefix = dataTagPrefix + this._config.dataTags.parentDataTag;
|
|
57
|
+
}
|
|
81
58
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
var attribName = attrib.name.replace(dataTagPrefix, "");
|
|
95
|
-
elementContent[attribName] = attrib.value;
|
|
59
|
+
if (!this._isTracked(element, dataTagPrefix, aiBlobAttributeTag)) {
|
|
60
|
+
// capture blob from element or hierarchy
|
|
61
|
+
biBlobValue = element.getAttribute(aiBlobAttributeTag);
|
|
62
|
+
if (biBlobValue) {
|
|
63
|
+
try {
|
|
64
|
+
elementContent = JSON.parse(biBlobValue);
|
|
65
|
+
} catch (e) {
|
|
66
|
+
this._traceLogger.throwInternal(
|
|
67
|
+
LoggingSeverity.CRITICAL,
|
|
68
|
+
_ExtendedInternalMessageId.CannotParseAiBlobValue, "Can not parse " + biBlobValue
|
|
69
|
+
);
|
|
96
70
|
}
|
|
71
|
+
} else {
|
|
72
|
+
// traverse up the DOM to find the closest parent with data-* tag defined
|
|
73
|
+
//contentElement = walkUpDomChainWithElementValidation(element, this._isTracked, dataTagPrefix);
|
|
74
|
+
elementContent = extend(elementContent, this._populateElementContent(element, dataTagPrefix, parentDataTagPrefix, aiBlobAttributeTag));
|
|
97
75
|
}
|
|
76
|
+
} else {
|
|
77
|
+
elementContent = extend(elementContent, this._populateElementContentwithDataTag(element, dataTagPrefix, parentDataTagPrefix, aiBlobAttributeTag));
|
|
78
|
+
}
|
|
79
|
+
removeInvalidElements(elementContent);
|
|
80
|
+
if (parentDataTagPrefix) {
|
|
81
|
+
elementContent = extend(elementContent, this._getParentDetails(element, elementContent, dataTagPrefix, aiBlobAttributeTag ));
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return elementContent;
|
|
85
|
+
}
|
|
98
86
|
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
let element = el;
|
|
104
|
-
let parentDataTagFound: boolean = false;
|
|
105
|
-
let elementLevelFlag: boolean = false; // Use this flag to capture 'id' only at the incoming html element level.
|
|
87
|
+
/**
|
|
88
|
+
* Capture current level Element content
|
|
89
|
+
*/
|
|
90
|
+
private _captureElementContentWithDataTag(contentElement: Element, elementContent: any, dataTagPrefix: string) {
|
|
106
91
|
|
|
107
|
-
|
|
108
|
-
|
|
92
|
+
for (var i = 0, attrib; i < contentElement.attributes.length; i++) {
|
|
93
|
+
attrib = contentElement.attributes[i];
|
|
109
94
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
if ( attrib.name.indexOf(dataTagPrefix) !== 0 ) {
|
|
114
|
-
continue;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
if (attrib.name.indexOf(parentDataTagPrefix) === 0) {
|
|
118
|
-
parentDataTagFound = true;
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
// Todo handle blob data
|
|
122
|
-
if (attrib.name.indexOf(aiBlobAttributeTag) === 0) {
|
|
123
|
-
continue;
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
const attribName = attrib.name.replace(dataTagPrefix, "");
|
|
127
|
-
if (elementLevelFlag && attribName === "id") {
|
|
128
|
-
continue; // skip capturing id if not at the first level.
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
if (!isValueAssigned(elementContent[attribName])) {
|
|
132
|
-
elementContent[attribName] = attrib.value;
|
|
133
|
-
}
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
// break after current level;
|
|
137
|
-
if (parentDataTagFound) {
|
|
138
|
-
break;
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
elementLevelFlag = true; // after the initial level set this flag to true.
|
|
142
|
-
element = (element.parentNode as Element);
|
|
143
|
-
}
|
|
95
|
+
if ( attrib.name.indexOf(dataTagPrefix) !== 0 ) {
|
|
96
|
+
continue;
|
|
144
97
|
}
|
|
145
98
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
let elementContent: any = {};
|
|
152
|
-
if(!element) {
|
|
153
|
-
return elementContent;
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
let htmlContent = _getHtmlIdAndContentName(element);
|
|
157
|
-
elementContent = {
|
|
158
|
-
id: htmlContent.id || "",
|
|
159
|
-
contentName: htmlContent.contentName || ""
|
|
160
|
-
};
|
|
161
|
-
|
|
162
|
-
if(isValueAssigned(parentDataTagPrefix)) {
|
|
163
|
-
_walkUpDomChainCaptureData(element, elementContent, dataTagPrefix, parentDataTagPrefix, aiBlobAttributeTag);
|
|
164
|
-
}
|
|
99
|
+
var attribName = attrib.name.replace(dataTagPrefix, "");
|
|
100
|
+
elementContent[attribName] = attrib.value;
|
|
101
|
+
}
|
|
102
|
+
}
|
|
165
103
|
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
104
|
+
/**
|
|
105
|
+
* Walk Up the DOM to capture Element content
|
|
106
|
+
*/
|
|
107
|
+
private _walkUpDomChainCaptureData(el: Element, elementContent: any, dataTagPrefix: string, parentDataTagPrefix: string, aiBlobAttributeTag: string ): void {
|
|
108
|
+
let element = el;
|
|
109
|
+
let parentDataTagFound: boolean = false;
|
|
110
|
+
let elementLevelFlag: boolean = false; // Use this flag to capture 'id' only at the incoming html element level.
|
|
111
|
+
while(!isNullOrUndefined(element) && !isNullOrUndefined(element.attributes)) {
|
|
112
|
+
let attributes=element.attributes;
|
|
113
|
+
for (let i = 0; i < attributes.length; i++) {
|
|
114
|
+
const attrib = attributes[i];
|
|
115
|
+
|
|
116
|
+
if ( attrib.name.indexOf(dataTagPrefix) !== 0 ) {
|
|
117
|
+
continue;
|
|
176
118
|
}
|
|
177
119
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
/**
|
|
182
|
-
* Capture Element content along with Data Tag attributes and values
|
|
183
|
-
*/
|
|
184
|
-
function _populateElementContentwithDataTag(element: Element, dataTagPrefix: string, parentDataTagPrefix: string, aiBlobAttributeTag: string) {
|
|
185
|
-
let dataTags = (_self._config || {}).dataTags;
|
|
186
|
-
let elementContent: any = {};
|
|
187
|
-
if(!element) {
|
|
188
|
-
return elementContent;
|
|
120
|
+
if( attrib.name.indexOf(parentDataTagPrefix) === 0) {
|
|
121
|
+
parentDataTagFound = true;
|
|
189
122
|
}
|
|
190
123
|
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
_walkUpDomChainCaptureData(element, elementContent, dataTagPrefix, parentDataTagPrefix, aiBlobAttributeTag);
|
|
195
|
-
} else {
|
|
196
|
-
_captureElementContentWithDataTag(element, elementContent, dataTagPrefix);
|
|
124
|
+
// Todo handle blob data
|
|
125
|
+
if( attrib.name.indexOf(aiBlobAttributeTag) === 0) {
|
|
126
|
+
continue;
|
|
197
127
|
}
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
if
|
|
201
|
-
|
|
202
|
-
elementContent.id = htmlContent.id || "";
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
elementContent.contentName = htmlContent.contentName || "";
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Validate to ensure the minimum required field 'id' or 'contentName' is present.
|
|
209
|
-
// The content schema defines id, aN and sN as required fields. However,
|
|
210
|
-
// requiring these fields would result in majority of adopter's content from being collected.
|
|
211
|
-
// Just throw a warning and continue collection.
|
|
212
|
-
if (!elementContent.id && !elementContent.contentName) {
|
|
213
|
-
_traceLogger.throwInternal(
|
|
214
|
-
LoggingSeverity.WARNING,
|
|
215
|
-
_ExtendedInternalMessageId.InvalidContentBlob, "Invalid content blob. Missing required attributes (id, contentName. " +
|
|
216
|
-
" Content information will still be collected!"
|
|
217
|
-
)
|
|
128
|
+
const attribName = attrib.name.replace(dataTagPrefix, "");
|
|
129
|
+
if(elementLevelFlag && attribName ==="id") continue; // skip capturing id if not at the first level.
|
|
130
|
+
if(!isValueAssigned(elementContent[attribName])) {
|
|
131
|
+
elementContent[attribName] = attrib.value;
|
|
218
132
|
}
|
|
219
|
-
|
|
220
|
-
return elementContent;
|
|
221
133
|
}
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
* @param captureAllMetaDataContent - Flag to capture all metadata content
|
|
227
|
-
* @param prefix - Prefix to search the metatags with.
|
|
228
|
-
* @param removePrefix - Specifies if the prefix must be excluded from key names in the returned collection.
|
|
229
|
-
* @returns Metadata collection/property bag
|
|
230
|
-
*/
|
|
231
|
-
function _getMetaDataFromDOM(captureAllMetaDataContent:boolean, prefix: string, removePrefix: boolean): { [name: string]: string } {
|
|
232
|
-
var metaElements: any;
|
|
233
|
-
var metaData = {};
|
|
234
|
-
if (hasDocument) {
|
|
235
|
-
metaElements = document.querySelectorAll("meta");
|
|
236
|
-
for (var i = 0; i < metaElements.length; i++) {
|
|
237
|
-
var meta = metaElements[i];
|
|
238
|
-
if (meta.name) {
|
|
239
|
-
if(captureAllMetaDataContent || meta.name.indexOf(prefix) === 0) {
|
|
240
|
-
const name = removePrefix ? meta.name.replace(prefix, "") : meta.name;
|
|
241
|
-
metaData[name] = meta.content;
|
|
242
|
-
}
|
|
243
|
-
}
|
|
244
|
-
}
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
return metaData;
|
|
134
|
+
|
|
135
|
+
// break after current level;
|
|
136
|
+
if(parentDataTagFound) {
|
|
137
|
+
break;
|
|
248
138
|
}
|
|
139
|
+
elementLevelFlag = true; // after the initial level set this flag to true.
|
|
140
|
+
element = (element.parentNode as Element);
|
|
141
|
+
}
|
|
142
|
+
}
|
|
249
143
|
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
144
|
+
/**
|
|
145
|
+
* Capture Element content along with Data Tag attributes and values
|
|
146
|
+
*/
|
|
147
|
+
private _populateElementContent(element: Element, dataTagPrefix: string, parentDataTagPrefix: string, aiBlobAttributeTag: string) {
|
|
148
|
+
|
|
149
|
+
let elementContent: any = {};
|
|
150
|
+
if(!element) return elementContent;
|
|
151
|
+
|
|
152
|
+
let htmlContent = this._getHtmlIdAndContentName(element);
|
|
153
|
+
elementContent = {
|
|
154
|
+
id: htmlContent.id || "",
|
|
155
|
+
contentName: htmlContent.contentName || ""
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
if(isValueAssigned(parentDataTagPrefix)) {
|
|
159
|
+
this._walkUpDomChainCaptureData(element, elementContent, dataTagPrefix, parentDataTagPrefix, aiBlobAttributeTag);
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// Validate to ensure the minimum required field 'id' or 'contentName' is present.
|
|
163
|
+
// The content schema defines id, aN and sN as required fields. However,
|
|
164
|
+
// requiring these fields would result in majority of adopter's content from being collected.
|
|
165
|
+
// Just throw a warning and continue collection.
|
|
166
|
+
if (!elementContent.id && !elementContent.contentName) {
|
|
167
|
+
this._traceLogger.throwInternal(
|
|
168
|
+
LoggingSeverity.WARNING,
|
|
169
|
+
_ExtendedInternalMessageId.InvalidContentBlob, "Invalid content blob. Missing required attributes (id, contentName. " +
|
|
170
|
+
" Content information will still be collected!"
|
|
171
|
+
)
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
return elementContent;
|
|
175
|
+
}
|
|
274
176
|
|
|
275
|
-
|
|
177
|
+
/**
|
|
178
|
+
* Capture Element content along with Data Tag attributes and values
|
|
179
|
+
*/
|
|
180
|
+
private _populateElementContentwithDataTag(element: Element, dataTagPrefix: string, parentDataTagPrefix: string, aiBlobAttributeTag: string) {
|
|
181
|
+
|
|
182
|
+
let elementContent: any = {};
|
|
183
|
+
if(!element) return elementContent;
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
let htmlContent = this._getHtmlIdAndContentName(element);
|
|
187
|
+
|
|
188
|
+
if(isValueAssigned(parentDataTagPrefix)) {
|
|
189
|
+
this._walkUpDomChainCaptureData(element, elementContent, dataTagPrefix, parentDataTagPrefix, aiBlobAttributeTag);
|
|
190
|
+
} else {
|
|
191
|
+
this._captureElementContentWithDataTag(element, elementContent, dataTagPrefix);
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
if (this._config.dataTags.useDefaultContentNameOrId) {
|
|
196
|
+
if(!isValueAssigned(elementContent.id)) {
|
|
197
|
+
elementContent.id = htmlContent.id || "";
|
|
276
198
|
}
|
|
199
|
+
elementContent.contentName = htmlContent.contentName || "";
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// Validate to ensure the minimum required field 'id' or 'contentName' is present.
|
|
203
|
+
// The content schema defines id, aN and sN as required fields. However,
|
|
204
|
+
// requiring these fields would result in majority of adopter's content from being collected.
|
|
205
|
+
// Just throw a warning and continue collection.
|
|
206
|
+
if (!elementContent.id && !elementContent.contentName) {
|
|
207
|
+
this._traceLogger.throwInternal(
|
|
208
|
+
LoggingSeverity.WARNING,
|
|
209
|
+
_ExtendedInternalMessageId.InvalidContentBlob, "Invalid content blob. Missing required attributes (id, contentName. " +
|
|
210
|
+
" Content information will still be collected!"
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
return elementContent;
|
|
215
|
+
}
|
|
216
|
+
|
|
277
217
|
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
218
|
+
/**
|
|
219
|
+
* Retrieve a specified metadata tag value from the DOM.
|
|
220
|
+
* @param captureAllMetaDataContent - Flag to capture all metadata content
|
|
221
|
+
* @param prefix - Prefix to search the metatags with.
|
|
222
|
+
* @param removePrefix - Specifies if the prefix must be excluded from key names in the returned collection.
|
|
223
|
+
* @returns Metadata collection/property bag
|
|
224
|
+
*/
|
|
225
|
+
private _getMetaDataFromDOM(captureAllMetaDataContent:boolean, prefix: string, removePrefix: boolean): { [name: string]: string } {
|
|
226
|
+
var metaElements: any;
|
|
227
|
+
var metaData = {};
|
|
228
|
+
if (hasDocument) {
|
|
229
|
+
metaElements = document.querySelectorAll("meta");
|
|
230
|
+
for (var i = 0; i < metaElements.length; i++) {
|
|
231
|
+
var meta = metaElements[i];
|
|
232
|
+
if (meta.name) {
|
|
233
|
+
if(captureAllMetaDataContent || meta.name.indexOf(prefix) === 0) {
|
|
234
|
+
const name = removePrefix ? meta.name.replace(prefix, "") : meta.name;
|
|
235
|
+
metaData[name] = meta.content;
|
|
293
236
|
}
|
|
294
237
|
}
|
|
295
|
-
return dataTagFound;
|
|
296
238
|
}
|
|
239
|
+
}
|
|
297
240
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
let callback = (_self._config || {}).callback;
|
|
301
|
-
let htmlContent: any = {};
|
|
302
|
-
if(!element) {
|
|
303
|
-
return htmlContent;
|
|
304
|
-
}
|
|
305
|
-
|
|
306
|
-
if (dataTags.useDefaultContentNameOrId) {
|
|
307
|
-
const customizedContentName = callback.contentName ? callback.contentName(element, dataTags.useDefaultContentNameOrId) : "";
|
|
308
|
-
const defaultContentName = _getDefaultContentName(element, dataTags.useDefaultContentNameOrId);
|
|
241
|
+
return metaData;
|
|
242
|
+
}
|
|
309
243
|
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
244
|
+
/**
|
|
245
|
+
* Gets the default content name.
|
|
246
|
+
* @param element - An html element
|
|
247
|
+
* @param useDefaultContentNameOrId -Flag indicating if an element is market PII.
|
|
248
|
+
* @returns Content name
|
|
249
|
+
*/
|
|
250
|
+
private _getDefaultContentName(element: any, useDefaultContentName: boolean) {
|
|
251
|
+
if (useDefaultContentName === false || !element.tagName) {
|
|
252
|
+
return "";
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
var doc = getDocument() || ({} as Document);
|
|
256
|
+
var contentName;
|
|
257
|
+
switch (element.tagName) {
|
|
258
|
+
case "A":
|
|
259
|
+
contentName = doc.all ? element.innerText || element.innerHTML : element.text || element.innerHTML;
|
|
260
|
+
break;
|
|
261
|
+
case "IMG":
|
|
262
|
+
case "AREA":
|
|
263
|
+
contentName = element.alt;
|
|
264
|
+
break;
|
|
265
|
+
default:
|
|
266
|
+
contentName = element.value || element.name || element.alt || element.innerText || element.id;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
return contentName.substring(0, MAX_CONTENTNAME_LENGTH);
|
|
270
|
+
}
|
|
315
271
|
|
|
316
|
-
|
|
272
|
+
/**
|
|
273
|
+
* Check if the user wants to track the element, which means if the element has any tags with data-* or customDataPrefix
|
|
274
|
+
* @param element - An html element
|
|
275
|
+
* @returns true if any data-* exist, otherwise return false
|
|
276
|
+
*/
|
|
277
|
+
private _isTracked(element: Element, dataTag: string, aiBlobAttributeTag: string): boolean {
|
|
278
|
+
const attrs = element.attributes;
|
|
279
|
+
let dataTagFound = false;
|
|
280
|
+
for (let i = 0; i < attrs.length; i++) {
|
|
281
|
+
const attributeName = attrs[i].name;
|
|
282
|
+
if(attributeName === aiBlobAttributeTag) {
|
|
283
|
+
// ignore if the attribute name is equal to aiBlobAttributeTag
|
|
284
|
+
return false;
|
|
285
|
+
} else if (attributeName.indexOf(dataTag) === 0) {
|
|
286
|
+
dataTagFound = true;
|
|
317
287
|
}
|
|
288
|
+
}
|
|
289
|
+
return dataTagFound;
|
|
290
|
+
}
|
|
318
291
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
* @returns An object containing the closest parentId , can be empty if nothing was found
|
|
323
|
-
*/
|
|
324
|
-
function _getParentDetails(element: Element, elementContent: any, dataTagPrefix: string, aiBlobAttributeTag: string): IContent {
|
|
325
|
-
const parentId = elementContent["parentid"];
|
|
326
|
-
const parentName = elementContent["parentname"];
|
|
327
|
-
let parentInfo = {};
|
|
328
|
-
|
|
329
|
-
if (parentId || parentName || !element) {
|
|
330
|
-
return parentInfo;
|
|
331
|
-
}
|
|
292
|
+
private _getHtmlIdAndContentName(element:Element) {
|
|
293
|
+
let htmlContent: any = {};
|
|
294
|
+
if(!element) return htmlContent;
|
|
332
295
|
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
* Check if parent info already set up, if so take and put into content, if not walk up the DOM to find correct info
|
|
337
|
-
* @param element - An html element that the user wants to track
|
|
338
|
-
* @returns An object containing the parent info, can be empty if nothing was found
|
|
339
|
-
*/
|
|
340
|
-
function _populateParentInfo(element: Element, dataTagPrefix: string, aiBlobAttributeTag: string): IContent {
|
|
341
|
-
let parentInfo: IContent = {};
|
|
342
|
-
let parentId;
|
|
343
|
-
|
|
344
|
-
// if the user does not set up parent info, walk to the DOM, find the closest parent element (with tags) and populate the info
|
|
345
|
-
const closestParentElement = walkUpDomChainWithElementValidation(element.parentElement, _isTracked, dataTagPrefix);
|
|
346
|
-
if (closestParentElement) {
|
|
347
|
-
const dataAttr = closestParentElement.getAttribute(aiBlobAttributeTag) || element[aiBlobAttributeTag];
|
|
348
|
-
if (dataAttr) {
|
|
349
|
-
try {
|
|
350
|
-
var telemetryObject = JSON.parse(dataAttr);
|
|
351
|
-
} catch (e) {
|
|
352
|
-
_traceLogger.throwInternal(
|
|
353
|
-
LoggingSeverity.CRITICAL,
|
|
354
|
-
_ExtendedInternalMessageId.CannotParseAiBlobValue, "Can not parse " + dataAttr
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
if (telemetryObject) {
|
|
358
|
-
parentId = telemetryObject.id;
|
|
359
|
-
}
|
|
360
|
-
} else {
|
|
361
|
-
parentId = closestParentElement.getAttribute(dataTagPrefix+"id");
|
|
362
|
-
}
|
|
363
|
-
}
|
|
364
|
-
if (parentId) {
|
|
365
|
-
parentInfo["parentid"] = parentId;
|
|
366
|
-
} else {
|
|
367
|
-
let htmlContent= _getHtmlIdAndContentName(element.parentElement);
|
|
368
|
-
parentInfo["parentid"] = htmlContent.id;
|
|
369
|
-
parentInfo["parentname"] = htmlContent.contentName;
|
|
370
|
-
}
|
|
296
|
+
if (this._config.dataTags.useDefaultContentNameOrId) {
|
|
297
|
+
const customizedContentName = this._config.callback.contentName ? this._config.callback.contentName(element, this._config.dataTags.useDefaultContentNameOrId) : "";
|
|
298
|
+
const defaultContentName = this._getDefaultContentName(element, this._config.dataTags.useDefaultContentNameOrId);
|
|
371
299
|
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
300
|
+
htmlContent = {
|
|
301
|
+
id: element.id,
|
|
302
|
+
contentName: customizedContentName || defaultContentName || element.getAttribute("alt")
|
|
303
|
+
};
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
return htmlContent;
|
|
375
307
|
}
|
|
376
308
|
|
|
377
309
|
/**
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
310
|
+
* Computes the parentId of a given element.
|
|
311
|
+
* @param element - An html element
|
|
312
|
+
* @returns An object containing the closest parentId , can be empty if nothing was found
|
|
313
|
+
*/
|
|
314
|
+
private _getParentDetails(element: Element, elementContent: any, dataTagPrefix: string, aiBlobAttributeTag: string): IContent {
|
|
315
|
+
const parentId = elementContent["parentid"];
|
|
316
|
+
const parentName = elementContent["parentname"];
|
|
317
|
+
let parentInfo = {};
|
|
318
|
+
|
|
319
|
+
if (parentId || parentName || !element) {
|
|
320
|
+
return parentInfo;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
return this._populateParentInfo(element, dataTagPrefix, aiBlobAttributeTag);
|
|
385
324
|
}
|
|
386
|
-
|
|
387
325
|
/**
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
326
|
+
* Check if parent info already set up, if so take and put into content, if not walk up the DOM to find correct info
|
|
327
|
+
* @param element - An html element that the user wants to track
|
|
328
|
+
* @returns An object containing the parent info, can be empty if nothing was found
|
|
329
|
+
*/
|
|
330
|
+
private _populateParentInfo(element: Element, dataTagPrefix: string, aiBlobAttributeTag: string): IContent {
|
|
331
|
+
let parentInfo: IContent = {};
|
|
332
|
+
let parentId;
|
|
333
|
+
|
|
334
|
+
// if the user does not set up parent info, walk to the DOM, find the closest parent element (with tags) and populate the info
|
|
335
|
+
const closestParentElement = walkUpDomChainWithElementValidation(element.parentElement, this._isTracked, dataTagPrefix);
|
|
336
|
+
if (closestParentElement) {
|
|
337
|
+
const dataAttr = closestParentElement.getAttribute(aiBlobAttributeTag) || element[aiBlobAttributeTag];
|
|
338
|
+
if (dataAttr) {
|
|
339
|
+
try {
|
|
340
|
+
var telemetryObject = JSON.parse(dataAttr);
|
|
341
|
+
} catch (e) {
|
|
342
|
+
this._traceLogger.throwInternal(
|
|
343
|
+
LoggingSeverity.CRITICAL,
|
|
344
|
+
_ExtendedInternalMessageId.CannotParseAiBlobValue, "Can not parse " + dataAttr
|
|
345
|
+
);
|
|
346
|
+
}
|
|
347
|
+
if (telemetryObject) {
|
|
348
|
+
parentId = telemetryObject.id;
|
|
349
|
+
}
|
|
350
|
+
} else {
|
|
351
|
+
parentId = closestParentElement.getAttribute(dataTagPrefix+"id");
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
if (parentId) {
|
|
355
|
+
parentInfo["parentid"] = parentId;
|
|
356
|
+
}
|
|
357
|
+
else {
|
|
358
|
+
let htmlContent= this._getHtmlIdAndContentName(element.parentElement);
|
|
359
|
+
parentInfo["parentid"] = htmlContent.id;
|
|
360
|
+
parentInfo["parentname"] = htmlContent.contentName;
|
|
361
|
+
}
|
|
362
|
+
return parentInfo;
|
|
396
363
|
}
|
|
397
364
|
}
|