forked from h5p/moodle-mod_hvp
-
Notifications
You must be signed in to change notification settings - Fork 1
/
xapi-collector.js
173 lines (155 loc) · 5.67 KB
/
xapi-collector.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
/**
* Collect results from xAPI events
*/
(function($) {
/**
* Finds a H5P library instance in an array based on the content ID
*
* @param {Array} instances
* @param {number} contentId
* @returns {Object|undefined} Content instance
*/
function findInstanceInArray(instances, contentId) {
if (instances !== undefined && contentId !== undefined) {
for (var i = 0; i < instances.length; i++) {
if (instances[i].contentId === contentId) {
return instances[i];
}
}
}
return undefined;
}
/**
* Finds the global instance from content id by looking through the DOM
*
* @param {number} [contentId] Content identifier
* @returns {Object} Content instance
*/
function getH5PInstance(contentId) {
var iframes;
var instance = null; // Returning null means no instance is found.
// No content id given, search for instance.
if (!contentId) {
instance = H5P.instances[0];
if (!instance) {
iframes = document.getElementsByClassName('h5p-iframe');
// Assume first iframe.
instance = iframes[0].contentWindow.H5P.instances[0];
}
} else {
// Try this documents instances.
instance = findInstanceInArray(H5P.instances, contentId);
if (!instance) {
// Locate iframes.
iframes = document.getElementsByClassName('h5p-iframe');
for (var i = 0; i < iframes.length; i++) {
// Search through each iframe for content.
instance = findInstanceInArray(iframes[i].contentWindow.H5P.instances, contentId);
if (instance) {
break;
}
}
}
}
return instance;
}
function getIframe(contentId) {
let iFrames;
// No content id given.
if (!contentId) {
iFrames = document.getElementsByClassName('h5p-iframe');
// Assume first iframe.
return iFrames[0];
}
// Locate iFrames.
iFrames = document.getElementsByClassName('h5p-iframe');
for (let i = 0; i < iFrames.length; i++) {
// Search through each iframe for content.
if (findInstanceInArray(iFrames[i].contentWindow.H5P.instances, contentId)) {
return iFrames[i];
}
}
return null;
}
/**
* Get xAPI data for content type and send off.
*
* @param {number} contentId Content id
* @param {Object} event Original xAPI event
*/
function storeXAPIData(contentId, event) {
var xAPIData;
var instance = getH5PInstance(contentId);
// Use getXAPIData contract, needed to get children.
if (instance && instance.getXAPIData) {
xAPIData = instance.getXAPIData();
} else {
// Fallback to event data.
xAPIData = {
statement: event.data.statement
};
}
// Ship the xAPI result.
var data = {
contentId: contentId,
xAPIResult: JSON.stringify(xAPIData)
};
$.post(H5PIntegration.ajax.xAPIResult, data).done(function (data) {
if (data.error) {
console.error('Storing xAPI results failed with error message:', data);
}
}).fail(function () {
if (H5P.offlineRequestQueue) {
H5P.offlineRequestQueue.add(H5PIntegration.ajax.xAPIResult, data);
return;
}
// Let H5P iframe know that we want to queue the request for late transmission.
const iframe = getIframe(contentId);
if (!iframe) {
return;
}
iframe.contentWindow.postMessage( {
url: H5PIntegration.ajax.xAPIResult,
data: data,
context: 'h5p',
action: 'queueRequest',
});
});
}
$(document).ready(function() {
// No external dispatcher.
if (!(window.H5P && H5P.externalDispatcher)) {
console.error('External dispatcher not found');
return;
}
// No ajax path.
if (!(window.H5PIntegration && H5PIntegration.ajax && H5PIntegration.ajax.xAPIResult)) {
console.error('No ajax path found');
return;
}
// Get emitted xAPI data.
H5P.externalDispatcher.on('xAPI', function(event) {
// Skip malformed events.
var hasStatement = event && event.data && event.data.statement;
if (!hasStatement) {
return;
}
var statement = event.data.statement;
var validVerb = statement.verb && statement.verb.display && statement.verb.display['en-US'];
if (!validVerb) {
return;
}
var isCompleted = statement.verb.display['en-US'] === 'answered' ||
statement.verb.display['en-US'] === 'completed';
var isChild = statement.context && statement.context.contextActivities &&
statement.context.contextActivities.parent &&
statement.context.contextActivities.parent[0] &&
statement.context.contextActivities.parent[0].id;
// Store only completed root events.
if (isCompleted && !isChild) {
// Get xAPI data with children if possible.
storeXAPIData(this.contentId, event);
}
});
});
})(H5P.jQuery);