forked from adlnet/xAPIWrapper
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathxapi-launch.js
More file actions
181 lines (172 loc) · 5.8 KB
/
xapi-launch.js
File metadata and controls
181 lines (172 loc) · 5.8 KB
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
174
175
176
177
178
179
180
181
(function(obj){
var ADL = obj;
function getQueryVariable(variable)
{
var query = window.location.search.substring(1);
var vars = query.split('&');
for (var i = 0; i < vars.length; i++)
{
var pair = vars[i].split('=');
if (decodeURIComponent(pair[0]) == variable)
{
return decodeURIComponent(pair[1]);
}
}
// console.log('Query variable %s not found', variable);
}
function cb_wrap(cb)
{
return function()
{
var args = arguments;
window.setTimeout(function()
{
var callerArgs = [];
for (var i = 0; i < args.length; i++)
{
callerArgs.push(args[i]);
}
cb.apply(window, callerArgs);
}, 0)
}
}
//The library will append the necessary launch info to each new A that is linked to the page.
//NOTE: This cannot work if you programmatically change the window location. If you do, you must
//execute the logic in setupCourseLinks to send the initialization data to the new location (if
//you wish that new location to track as part of this session)
function observeForNewLinks()
{
// select the target node
var target = document.body;
// create an observer instance
var observer = new MutationObserver(function(mutations)
{
mutations.forEach(function(mutation)
{
for (var i in mutation.addedNodes)
{
if (mutation.addedNodes.hasOwnProperty(i))
{
if (mutation.addedNodes[i].constructor == HTMLAnchorElement) {
var node = mutation.addedNodes[i];
setupCourseLinks([node]);
}
}
}
});
});
// configuration of the observer:
var config = {
attributes: true,
childList: true,
subtree: true
};
// pass in the target node, as well as the observer options
observer.observe(target, config);
// later, you can stop observing
/// observer.disconnect();
}
//This library will init all links in the DOM that include the attribute "courseLink = true"
//with the information necessary for the document at that link to track as part of this session.
function setupCourseLinks(_nodes)
{
var launchToken = getQueryVariable("xAPILaunchKey");
var launchEndpoint = getQueryVariable("xAPILaunchService");
var encrypted = getQueryVariable("encrypted");
var query = "xAPILaunchKey=" + launchToken + "&xAPILaunchService=" + launchEndpoint;
if (encrypted)
{
query += "&encrypted=true";
}
for(var i = 0; i < _nodes.length; i++)
{
var link = _nodes[i];
var href = link.href;
var courseLink = link.attributes.getNamedItem('courselink')
if (courseLink && courseLink.value == "true")
{
if (href.indexOf("?") > -1)
{
href = href + "&" + query;
}
else
href = href + "?" + query;
link.href = href;
}
};
}
function xAPILaunch(cb, terminate_on_unload, strict_callbacks)
{
cb = cb_wrap(cb);
try
{
var launchToken = getQueryVariable("xAPILaunchKey");
var launchEndpoint = getQueryVariable("xAPILaunchService");
var encrypted = getQueryVariable("encrypted");
if (encrypted)
{
//here, we'd have to implement decryption for the data. This makes little sense in a client side only course
}
xAPILaunch.terminate = function(message)
{
var launch = new URL(launchEndpoint);
launch.pathname += "launch/" + launchToken + "/terminate";
var xhr2 = new XMLHttpRequest();
xhr2.withCredentials = true;
xhr2.crossDomain = true;
xhr2.open('POST', launch.toString(), false);
xhr2.setRequestHeader("Content-type" , "application/json");
xhr2.send(JSON.stringify({"code":0,"description": message ||"User closed content"}));
}
if (!launchToken || !launchEndpoint)
return cb("invalid launch parameters");
var launch = new URL(launchEndpoint);
launch.pathname += "launch/" + launchToken;
var xhr = new XMLHttpRequest();
xhr.withCredentials = true;
xhr.crossDomain = true;
xhr.onerror = function(err)
{
//exit the try catch so inner execptions in the callback don't trigger this catch
window.setTimeout(function()
{
return cb(err);
})
}
xhr.onload = function(e)
{
if (xhr.status !== 200)
{
return xhr.onerror(xhr.responseText);
}
var body = JSON.parse(xhr.responseText);
var launchData = body;
var conf = {};
conf['endpoint'] = launchData.endpoint;
conf["actor"] = launchData.actor;
conf.withCredentials = true;
conf.strictCallbacks = strict_callbacks || false;
window.onunload = function()
{
if (!terminate_on_unload)
return;
xAPILaunch.terminate("User closed content")
}
var wrapper = new ADL.XAPIWrapper.constructor();
wrapper.changeConfig(conf);
//Links that include "courseLink='true'"
setupCourseLinks(document.body.querySelectorAll('a'));
//Also, if links are added dynamically, we will do the same logic for those links.
observeForNewLinks();
return cb(null, body, wrapper);
}
xhr.open('POST', launch.toString(), true);
xhr.send();
}
catch (e)
{
cb(e);
}
};
ADL.launch = xAPILaunch;
})(window.ADL = window.ADL || {});