fcad-core-dragon 2.1.0-beta.1 → 2.1.0-beta.3
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/CHANGELOG +40 -0
- package/package.json +30 -31
- package/src/components/AppBase.vue +167 -39
- package/src/components/AppBaseButton.test.js +0 -1
- package/src/components/AppBaseModule.vue +103 -116
- package/src/components/AppBasePage.vue +13 -13
- package/src/components/AppCompInputCheckBoxNx.vue +1 -1
- package/src/components/AppCompInputRadioNx.vue +1 -1
- package/src/components/AppCompMenu.vue +2 -1
- package/src/components/AppCompPlayBarNext.vue +157 -16
- package/src/components/AppCompPopUpNext.vue +3 -3
- package/src/components/AppCompQuizNext.vue +1 -1
- package/src/components/AppCompQuizRecall.vue +2 -3
- package/src/components/AppCompTableOfContent.vue +1 -1
- package/src/components/tests__/useTimer.spec.js +91 -0
- package/src/composables/useIdleDetector.js +56 -0
- package/src/composables/useQuiz.js +1 -1
- package/src/composables/useTimer.js +175 -0
- package/src/main.js +2 -0
- package/src/module/stores/appStore.js +10 -34
- package/src/module/xapi/ADL.js +1 -0
- package/src/plugins/analytics.js +34 -0
- package/src/plugins/i18n.js +9 -27
- package/src/router/index.js +6 -3
- package/src/components/AppCompPlayBarProgress.vue +0 -82
- package/src/mixins/$mediaMixins.js +0 -809
- package/src/mixins/timerMixin.js +0 -195
- package/src/module/xapi/wrapper copy.js +0 -1963
|
@@ -1,1963 +0,0 @@
|
|
|
1
|
-
import { CryptoJS } from './Crypto'
|
|
2
|
-
|
|
3
|
-
/*
|
|
4
|
-
* Wrapper Ref: https://github.com/adlnet/xAPIWrapper
|
|
5
|
-
*/
|
|
6
|
-
export function xapiwrapper(ADL) {
|
|
7
|
-
//==============================================================================
|
|
8
|
-
// adds toISOString to date objects if not there
|
|
9
|
-
// from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
|
|
10
|
-
|
|
11
|
-
if (!Date.prototype.toISOString) {
|
|
12
|
-
;(function () {
|
|
13
|
-
function pad(number) {
|
|
14
|
-
let r = String(number)
|
|
15
|
-
if (r.length === 1) {
|
|
16
|
-
r = '0' + r
|
|
17
|
-
}
|
|
18
|
-
return r
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
Date.prototype.toISOString = function () {
|
|
22
|
-
return (
|
|
23
|
-
this.getUTCFullYear() +
|
|
24
|
-
'-' +
|
|
25
|
-
pad(this.getUTCMonth() + 1) +
|
|
26
|
-
'-' +
|
|
27
|
-
pad(this.getUTCDate()) +
|
|
28
|
-
'T' +
|
|
29
|
-
pad(this.getUTCHours()) +
|
|
30
|
-
':' +
|
|
31
|
-
pad(this.getUTCMinutes()) +
|
|
32
|
-
':' +
|
|
33
|
-
pad(this.getUTCSeconds()) +
|
|
34
|
-
'.' +
|
|
35
|
-
String((this.getUTCMilliseconds() / 1000).toFixed(3)).slice(2, 5) +
|
|
36
|
-
'Z'
|
|
37
|
-
)
|
|
38
|
-
}
|
|
39
|
-
})()
|
|
40
|
-
}
|
|
41
|
-
// shim for old-style Base64 lib
|
|
42
|
-
function toBase64(text) {
|
|
43
|
-
if (CryptoJS && CryptoJS.enc.Base64)
|
|
44
|
-
return CryptoJS.enc.Base64.stringify(CryptoJS.enc.Latin1.parse(text))
|
|
45
|
-
// else return Base64.encode(text)
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
// shim for old-style crypto lib
|
|
49
|
-
function toSHA1(text) {
|
|
50
|
-
if (CryptoJS && CryptoJS.SHA1) return CryptoJS.SHA1(text).toString()
|
|
51
|
-
else return Crypto.util.bytesToHex(Crypto.SHA1(text, { asBytes: true }))
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
function toSHA256(content) {
|
|
55
|
-
if (Object.prototype.toString.call(content) !== '[object ArrayBuffer]') {
|
|
56
|
-
return CryptoJS.SHA256(content).toString(CryptoJS.enc.Hex)
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
// Create a WordArray from the ArrayBuffer.
|
|
60
|
-
let i8a = new Uint8Array(content)
|
|
61
|
-
let a = []
|
|
62
|
-
for (let i = 0; i < i8a.length; i += 4) {
|
|
63
|
-
a.push(
|
|
64
|
-
(i8a[i] << 24) | (i8a[i + 1] << 16) | (i8a[i + 2] << 8) | i8a[i + 3]
|
|
65
|
-
)
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return CryptoJS.SHA256(
|
|
69
|
-
CryptoJS.lib.WordArray.create(a, i8a.length)
|
|
70
|
-
).toString(CryptoJS.enc.Hex)
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
// check if string or object is date, if it is, return date object
|
|
74
|
-
// feburary 31st == march 3rd in this solution
|
|
75
|
-
function isDate(date) {
|
|
76
|
-
// check if object is being passed
|
|
77
|
-
let d
|
|
78
|
-
if (Object.prototype.toString.call(date) === '[object Date]') d = date
|
|
79
|
-
else d = new Date(date)
|
|
80
|
-
// deep check on date object
|
|
81
|
-
if (Object.prototype.toString.call(d) === '[object Date]') {
|
|
82
|
-
// it is a date
|
|
83
|
-
if (isNaN(d.valueOf())) {
|
|
84
|
-
ADL.XAPIWrapper.log('Invalid date String passed')
|
|
85
|
-
return null
|
|
86
|
-
} else {
|
|
87
|
-
return d
|
|
88
|
-
}
|
|
89
|
-
} else {
|
|
90
|
-
// not a date
|
|
91
|
-
ADL.XAPIWrapper.log('Invalid date object')
|
|
92
|
-
return null
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
//////////////////////////////////////////////////////////////////////
|
|
96
|
-
log.debug = false
|
|
97
|
-
|
|
98
|
-
function getByteLen(normal_val) {
|
|
99
|
-
// Force string type
|
|
100
|
-
normal_val = String(normal_val)
|
|
101
|
-
|
|
102
|
-
let byteLen = 0
|
|
103
|
-
for (let i = 0; i < normal_val.length; i++) {
|
|
104
|
-
let c = normal_val.charCodeAt(i)
|
|
105
|
-
byteLen +=
|
|
106
|
-
c < 1 << 7
|
|
107
|
-
? 1
|
|
108
|
-
: c < 1 << 11
|
|
109
|
-
? 2
|
|
110
|
-
: c < 1 << 16
|
|
111
|
-
? 3
|
|
112
|
-
: c < 1 << 21
|
|
113
|
-
? 4
|
|
114
|
-
: c < 1 << 26
|
|
115
|
-
? 5
|
|
116
|
-
: c < 1 << 31
|
|
117
|
-
? 6
|
|
118
|
-
: Number.NaN
|
|
119
|
-
}
|
|
120
|
-
return byteLen
|
|
121
|
-
}
|
|
122
|
-
getByteLen()
|
|
123
|
-
/*
|
|
124
|
-
* Config object used w/ url params to configure the lrs object
|
|
125
|
-
* change these to match your lrs
|
|
126
|
-
* @return {object} config object
|
|
127
|
-
* @example
|
|
128
|
-
* let conf = {
|
|
129
|
-
* "endpoint" : "https://lrs.adlnet.gov/xapi/",
|
|
130
|
-
* "auth" : "Basic " + toBase64('tom:1234'),
|
|
131
|
-
* };
|
|
132
|
-
* ADL.XAPIWrapper.changeConfig(conf);
|
|
133
|
-
*/
|
|
134
|
-
let Config = (function () {
|
|
135
|
-
let conf = {}
|
|
136
|
-
conf['endpoint'] = 'http://localhost:8000/xapi/'
|
|
137
|
-
//try
|
|
138
|
-
//{
|
|
139
|
-
conf['auth'] = 'Basic ' + toBase64('tom:1234')
|
|
140
|
-
//}
|
|
141
|
-
//catch (e)
|
|
142
|
-
//{
|
|
143
|
-
// log("Exception in Config trying to encode auth: " + e);
|
|
144
|
-
//}
|
|
145
|
-
|
|
146
|
-
// Statement defaults
|
|
147
|
-
// conf["actor"] = {"mbox":"default@example.com"};
|
|
148
|
-
// conf["registration"] = ruuid();
|
|
149
|
-
// conf["grouping"] = {"id":"ctxact:default/grouping"};
|
|
150
|
-
// conf["activity_platform"] = "default platform";
|
|
151
|
-
|
|
152
|
-
// Behavior defaults
|
|
153
|
-
// conf["strictCallbacks"] = false; // Strict error-first callbacks
|
|
154
|
-
return conf
|
|
155
|
-
})()
|
|
156
|
-
/*
|
|
157
|
-
* XAPIWrapper Constructor
|
|
158
|
-
* @param {object} config with a minimum of an endoint property
|
|
159
|
-
* @param {boolean} verifyxapiversion indicating whether to verify the version of the LRS is compatible with this wrapper
|
|
160
|
-
*/
|
|
161
|
-
let XAPIWrapper = function (config, verifyxapiversion) {
|
|
162
|
-
this.lrs = getLRSObject(config || {})
|
|
163
|
-
if (this.lrs.user && this.lrs.password)
|
|
164
|
-
updateAuth(this.lrs, this.lrs.user, this.lrs.password)
|
|
165
|
-
this.base = getbase(this.lrs.endpoint)
|
|
166
|
-
|
|
167
|
-
this.withCredentials = false
|
|
168
|
-
if (config && typeof config.withCredentials != 'undefined') {
|
|
169
|
-
this.withCredentials = config.withCredentials
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Ensure that callbacks are always executed, first param is error (null if no error) followed
|
|
173
|
-
// by the result(s)
|
|
174
|
-
this.strictCallbacks = false
|
|
175
|
-
this.strictCallbacks = config && config.strictCallbacks
|
|
176
|
-
|
|
177
|
-
function getbase(url) {
|
|
178
|
-
let l = document.createElement('a')
|
|
179
|
-
l.href = url
|
|
180
|
-
if (l.protocol && l.host) {
|
|
181
|
-
return l.protocol + '//' + l.host
|
|
182
|
-
} else if (l.href) {
|
|
183
|
-
// IE 11 fix.
|
|
184
|
-
let parts = l.href.split('//')
|
|
185
|
-
return parts[0] + '//' + parts[1].substring(0, parts[1].indexOf('/'))
|
|
186
|
-
} else
|
|
187
|
-
ADL.XAPIWrapper.log("Couldn't create base url from endpoint: " + url)
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
function updateAuth(obj, username, password) {
|
|
191
|
-
obj.auth = 'Basic ' + toBase64(username + ':' + password)
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
if (verifyxapiversion && testConfig.call(this)) {
|
|
195
|
-
window.ADL.XHR_request(
|
|
196
|
-
this.lrs,
|
|
197
|
-
this.lrs.endpoint + 'about',
|
|
198
|
-
'GET',
|
|
199
|
-
null,
|
|
200
|
-
null,
|
|
201
|
-
function (r) {
|
|
202
|
-
if (r.status == 200) {
|
|
203
|
-
try {
|
|
204
|
-
let lrsabout = JSON.parse(r.response)
|
|
205
|
-
let versionOK = false
|
|
206
|
-
for (let idx in lrsabout.version) {
|
|
207
|
-
if (lrsabout.version.hasOwnProperty(idx))
|
|
208
|
-
if (lrsabout.version[idx] == ADL.XAPIWrapper.xapiVersion) {
|
|
209
|
-
versionOK = true
|
|
210
|
-
break
|
|
211
|
-
}
|
|
212
|
-
}
|
|
213
|
-
if (!versionOK) {
|
|
214
|
-
ADL.XAPIWrapper.log(
|
|
215
|
-
'The lrs version [' +
|
|
216
|
-
lrsabout.version +
|
|
217
|
-
']' +
|
|
218
|
-
" does not match this wrapper's XAPI version [" +
|
|
219
|
-
ADL.XAPIWrapper.xapiVersion +
|
|
220
|
-
']'
|
|
221
|
-
)
|
|
222
|
-
}
|
|
223
|
-
} catch (e) {
|
|
224
|
-
ADL.XAPIWrapper.log('The response was not an about object')
|
|
225
|
-
}
|
|
226
|
-
} else {
|
|
227
|
-
ADL.XAPIWrapper.log(
|
|
228
|
-
'The request to get information about the LRS failed: ' + r
|
|
229
|
-
)
|
|
230
|
-
}
|
|
231
|
-
},
|
|
232
|
-
null,
|
|
233
|
-
false,
|
|
234
|
-
null,
|
|
235
|
-
this.withCredentials,
|
|
236
|
-
false
|
|
237
|
-
)
|
|
238
|
-
}
|
|
239
|
-
|
|
240
|
-
this.searchParams = function () {
|
|
241
|
-
let sp = { format: 'exact' }
|
|
242
|
-
return sp
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
this.hash = function (tohash) {
|
|
246
|
-
if (!tohash) return null
|
|
247
|
-
try {
|
|
248
|
-
return toSHA1(tohash)
|
|
249
|
-
} catch (e) {
|
|
250
|
-
ADL.XAPIWrapper.log('Error trying to hash -- ' + e)
|
|
251
|
-
return null
|
|
252
|
-
}
|
|
253
|
-
}
|
|
254
|
-
|
|
255
|
-
this.changeConfig = function (config) {
|
|
256
|
-
try {
|
|
257
|
-
ADL.XAPIWrapper.log('updating lrs object with new configuration')
|
|
258
|
-
this.lrs = mergeRecursive(this.lrs, config)
|
|
259
|
-
if (config.user && config.password)
|
|
260
|
-
this.updateAuth(this.lrs, config.user, config.password)
|
|
261
|
-
this.base = getbase(this.lrs.endpoint)
|
|
262
|
-
this.withCredentials = config.withCredentials
|
|
263
|
-
this.strictCallbacks = config.strictCallbacks
|
|
264
|
-
} catch (e) {
|
|
265
|
-
ADL.XAPIWrapper.log('error while changing configuration -- ' + e)
|
|
266
|
-
}
|
|
267
|
-
}
|
|
268
|
-
|
|
269
|
-
this.updateAuth = updateAuth
|
|
270
|
-
}
|
|
271
|
-
|
|
272
|
-
// This wrapper is based on the Experience API Spec version:
|
|
273
|
-
XAPIWrapper.prototype.xapiVersion = '1.0.1'
|
|
274
|
-
|
|
275
|
-
/*
|
|
276
|
-
* Adds info from the lrs object to the statement, if available.
|
|
277
|
-
* These values could be initialized from the Config object or from the url query string.
|
|
278
|
-
* @param {object} stmt the statement object
|
|
279
|
-
*/
|
|
280
|
-
XAPIWrapper.prototype.prepareStatement = function (stmt) {
|
|
281
|
-
if (stmt.actor === undefined) {
|
|
282
|
-
stmt.actor = JSON.parse(this.lrs.actor)
|
|
283
|
-
} else if (typeof stmt.actor === 'string') {
|
|
284
|
-
stmt.actor = JSON.parse(stmt.actor)
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
if (
|
|
288
|
-
this.lrs.grouping ||
|
|
289
|
-
this.lrs.registration ||
|
|
290
|
-
this.lrs.activity_platform
|
|
291
|
-
) {
|
|
292
|
-
if (!stmt.context) {
|
|
293
|
-
stmt.context = {}
|
|
294
|
-
}
|
|
295
|
-
}
|
|
296
|
-
|
|
297
|
-
if (this.lrs.grouping) {
|
|
298
|
-
if (!stmt.context.contextActivities) {
|
|
299
|
-
stmt.context.contextActivities = {}
|
|
300
|
-
}
|
|
301
|
-
|
|
302
|
-
// PR from brian-learningpool to resolve context overwriting
|
|
303
|
-
if (!Array.isArray(stmt.context.contextActivities.grouping)) {
|
|
304
|
-
stmt.context.contextActivities.grouping = [{ id: this.lrs.grouping }]
|
|
305
|
-
} else {
|
|
306
|
-
stmt.context.contextActivities.grouping.splice(0, 0, {
|
|
307
|
-
id: this.lrs.grouping
|
|
308
|
-
})
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
if (this.lrs.registration) {
|
|
312
|
-
stmt.context.registration = this.lrs.registration
|
|
313
|
-
}
|
|
314
|
-
if (this.lrs.activity_platform) {
|
|
315
|
-
stmt.context.platform = this.lrs.activity_platform
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
// tests the configuration of the lrs object
|
|
320
|
-
XAPIWrapper.prototype.testConfig = testConfig
|
|
321
|
-
|
|
322
|
-
// writes to the console if available
|
|
323
|
-
XAPIWrapper.prototype.log = log
|
|
324
|
-
|
|
325
|
-
// Default encoding
|
|
326
|
-
XAPIWrapper.prototype.defaultEncoding = 'utf-8'
|
|
327
|
-
|
|
328
|
-
/*
|
|
329
|
-
* Send a single statement to the LRS. Makes a Javascript object
|
|
330
|
-
* with the statement id as 'id' available to the callback function.
|
|
331
|
-
* @param {object} stmt statement object to send
|
|
332
|
-
* @param {function} [callback] function to be called after the LRS responds
|
|
333
|
-
* to this request (makes the call asynchronous)
|
|
334
|
-
* the function will be passed the XMLHttpRequest object
|
|
335
|
-
* and an object with an id property assigned the id
|
|
336
|
-
* of the statement
|
|
337
|
-
* @return {object} object containing xhr object and id of statement
|
|
338
|
-
* @example
|
|
339
|
-
* // Send Statement
|
|
340
|
-
* let stmt = {"actor" : {"mbox" : "mailto:tom@example.com"},
|
|
341
|
-
* "verb" : {"id" : "http://adlnet.gov/expapi/verbs/answered",
|
|
342
|
-
* "display" : {"en-US" : "answered"}},
|
|
343
|
-
* "object" : {"id" : "http://adlnet.gov/expapi/activities/question"}};
|
|
344
|
-
* let resp_obj = ADL.XAPIWrapper.sendStatement(stmt);
|
|
345
|
-
* ADL.XAPIWrapper.log("[" + resp_obj.id + "]: " + resp_obj.xhr.status + " - " + resp_obj.xhr.statusText);
|
|
346
|
-
* >> [3e616d1c-5394-42dc-a3aa-29414f8f0dfe]: 204 - NO CONTENT
|
|
347
|
-
*
|
|
348
|
-
* // Send Statement with Callback
|
|
349
|
-
* let stmt = {"actor" : {"mbox" : "mailto:tom@example.com"},
|
|
350
|
-
* "verb" : {"id" : "http://adlnet.gov/expapi/verbs/answered",
|
|
351
|
-
* "display" : {"en-US" : "answered"}},
|
|
352
|
-
* "object" : {"id" : "http://adlnet.gov/expapi/activities/question"}};
|
|
353
|
-
* ADL.XAPIWrapper.sendStatement(stmt, function(resp, obj){
|
|
354
|
-
* ADL.XAPIWrapper.log("[" + obj.id + "]: " + resp.status + " - " + resp.statusText);});
|
|
355
|
-
* >> [4edfe763-8b84-41f1-a355-78b7601a6fe8]: 204 - NO CONTENT
|
|
356
|
-
*/
|
|
357
|
-
XAPIWrapper.prototype.sendStatement = function (stmt, callback, attachments) {
|
|
358
|
-
if (this.testConfig()) {
|
|
359
|
-
this.prepareStatement(stmt)
|
|
360
|
-
let id
|
|
361
|
-
if (stmt['id']) {
|
|
362
|
-
id = stmt['id']
|
|
363
|
-
} else {
|
|
364
|
-
id = ADL.ruuid()
|
|
365
|
-
stmt['id'] = id
|
|
366
|
-
}
|
|
367
|
-
|
|
368
|
-
let payload = JSON.stringify(stmt)
|
|
369
|
-
let extraHeaders = null
|
|
370
|
-
if (attachments && attachments.length > 0) {
|
|
371
|
-
extraHeaders = {}
|
|
372
|
-
payload = this.buildMultipartPost(stmt, attachments, extraHeaders)
|
|
373
|
-
}
|
|
374
|
-
let resp = ADL.XHR_request(
|
|
375
|
-
this.lrs,
|
|
376
|
-
this.lrs.endpoint + 'statements',
|
|
377
|
-
'POST',
|
|
378
|
-
payload,
|
|
379
|
-
this.lrs.auth,
|
|
380
|
-
callback,
|
|
381
|
-
{ id: id },
|
|
382
|
-
null,
|
|
383
|
-
extraHeaders,
|
|
384
|
-
this.withCredentials,
|
|
385
|
-
this.strictCallbacks
|
|
386
|
-
)
|
|
387
|
-
if (!callback) return { xhr: resp, id: id }
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
/*
|
|
392
|
-
* Custome method to send statement to the LRS using the fech API instead of XMLHttpRequest. Makes a Javascript object
|
|
393
|
-
* with the statement id as 'id' available to the callback function.
|
|
394
|
-
* @param {object} stmt statement object to send
|
|
395
|
-
* @param {function} [callback] function to be called after the LRS responds
|
|
396
|
-
* to this request (makes the call asynchronous)
|
|
397
|
-
* @return {object} object containing xhr object and id of statement
|
|
398
|
-
* @example
|
|
399
|
-
* // Send Statement
|
|
400
|
-
* let stmt = {"actor" : {"mbox" : "mailto:tom@example.com"},
|
|
401
|
-
* "verb" : {"id" : "http://adlnet.gov/expapi/verbs/answered",
|
|
402
|
-
* "display" : {"en-US" : "answered"}},
|
|
403
|
-
* "object" : {"id" : "http://adlnet.gov/expapi/activities/question"}};
|
|
404
|
-
* let resp_obj = ADL.XAPIWrapper.sendStatementWithFetch(stmt);
|
|
405
|
-
* ADL.XAPIWrapper.log("[" + resp_obj.id + "]: " + resp_obj.xhr.status + " - " + resp_obj.xhr.statusText);
|
|
406
|
-
* >> [3e616d1c-5394-42dc-a3aa-29414f8f0dfe]: 204 - NO CONTENT
|
|
407
|
-
*
|
|
408
|
-
* // Send Statement with Callback
|
|
409
|
-
* let stmt = {"actor" : {"mbox" : "mailto:tom@example.com"},
|
|
410
|
-
* "verb" : {"id" : "http://adlnet.gov/expapi/verbs/answered",
|
|
411
|
-
* "display" : {"en-US" : "answered"}},
|
|
412
|
-
* "object" : {"id" : "http://adlnet.gov/expapi/activities/question"}};
|
|
413
|
-
* ADL.XAPIWrapper.sendStatementWithFetch(stmt, function(resp, obj){
|
|
414
|
-
* ADL.XAPIWrapper.log("[" + obj.id + "]: " + resp.status + " - " + resp.statusText);});
|
|
415
|
-
* >> [4edfe763-8b84-41f1-a355-78b7601a6fe8]: 204 - NO CONTENT
|
|
416
|
-
*/
|
|
417
|
-
XAPIWrapper.prototype.sendStatementWithFetch = async function (
|
|
418
|
-
stmt,
|
|
419
|
-
callback,
|
|
420
|
-
attachments
|
|
421
|
-
) {
|
|
422
|
-
if (this.testConfig()) {
|
|
423
|
-
this.prepareStatement(stmt)
|
|
424
|
-
let id
|
|
425
|
-
if (stmt['id']) {
|
|
426
|
-
id = stmt['id']
|
|
427
|
-
} else {
|
|
428
|
-
id = ADL.ruuid()
|
|
429
|
-
stmt['id'] = id
|
|
430
|
-
}
|
|
431
|
-
let headers = {}
|
|
432
|
-
headers['Content-type'] = 'application/json; charset=UTF-8'
|
|
433
|
-
headers['Authorization'] = this.lrs.auth
|
|
434
|
-
headers['X-Experience-API-Version'] = ADL.XAPIWrapper.xapiVersion
|
|
435
|
-
|
|
436
|
-
let payload = JSON.stringify(stmt)
|
|
437
|
-
let extraHeaders = null
|
|
438
|
-
if (attachments && attachments.length > 0) {
|
|
439
|
-
extraHeaders = {}
|
|
440
|
-
payload = this.buildMultipartPost(stmt, attachments, extraHeaders)
|
|
441
|
-
}
|
|
442
|
-
|
|
443
|
-
// fecth API used to allow the delivery of the request after the browser is closed
|
|
444
|
-
let resp = await fetch(this.lrs.endpoint + 'statements', {
|
|
445
|
-
method: 'POST',
|
|
446
|
-
headers,
|
|
447
|
-
body: payload,
|
|
448
|
-
keepalive: true // allow the request to outlive the closing of browser tab
|
|
449
|
-
})
|
|
450
|
-
|
|
451
|
-
if (!callback) return { xhr: resp, id: id }
|
|
452
|
-
}
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
XAPIWrapper.prototype.stringToArrayBuffer = function (content, encoding) {
|
|
456
|
-
encoding = encoding || ADL.XAPIWrapper.defaultEncoding
|
|
457
|
-
|
|
458
|
-
return new TextEncoder(encoding).encode(content).buffer
|
|
459
|
-
}
|
|
460
|
-
|
|
461
|
-
XAPIWrapper.prototype.stringFromArrayBuffer = function (content, encoding) {
|
|
462
|
-
encoding = encoding || ADL.XAPIWrapper.defaultEncoding
|
|
463
|
-
|
|
464
|
-
return new TextDecoder(encoding).decode(content)
|
|
465
|
-
}
|
|
466
|
-
|
|
467
|
-
/*
|
|
468
|
-
* Build the post body to include the multipart boundries, edit the statement to include the attachment types
|
|
469
|
-
* extraHeaders should be an object. It will have the multipart boundary value set
|
|
470
|
-
* attachments should be an array of objects of the type
|
|
471
|
-
* {
|
|
472
|
-
type:"signature" || {
|
|
473
|
-
usageType : URI,
|
|
474
|
-
display: Language-map
|
|
475
|
-
description: Language-map
|
|
476
|
-
},
|
|
477
|
-
value : a UTF8 string containing the binary data of the attachment. For string values, this can just be the JS string.
|
|
478
|
-
}
|
|
479
|
-
*/
|
|
480
|
-
XAPIWrapper.prototype.buildMultipartPost = function (
|
|
481
|
-
statement,
|
|
482
|
-
attachments,
|
|
483
|
-
extraHeaders
|
|
484
|
-
) {
|
|
485
|
-
statement.attachments = []
|
|
486
|
-
for (let i = 0; i < attachments.length; i++) {
|
|
487
|
-
// Replace the term 'signature' with the hard coded definition for a signature attachment
|
|
488
|
-
if (attachments[i].type == 'signature') {
|
|
489
|
-
attachments[i].type = {
|
|
490
|
-
usageType: 'http://adlnet.gov/expapi/attachments/signature',
|
|
491
|
-
display: {
|
|
492
|
-
'en-US': 'A JWT signature'
|
|
493
|
-
},
|
|
494
|
-
description: {
|
|
495
|
-
'en-US': 'A signature proving the statement was not modified'
|
|
496
|
-
},
|
|
497
|
-
contentType: 'application/octet-stream'
|
|
498
|
-
}
|
|
499
|
-
}
|
|
500
|
-
|
|
501
|
-
if (typeof attachments[i].value === 'string') {
|
|
502
|
-
// Convert the string value to an array buffer.
|
|
503
|
-
attachments[i].value = this.stringToArrayBuffer(attachments[i].value)
|
|
504
|
-
}
|
|
505
|
-
|
|
506
|
-
// Compute the length and the sha2 of the attachment
|
|
507
|
-
attachments[i].type.length = attachments[i].value.byteLength
|
|
508
|
-
attachments[i].type.sha2 = toSHA256(attachments[i].value)
|
|
509
|
-
|
|
510
|
-
// Attach the attachment metadata to the statement.
|
|
511
|
-
statement.attachments.push(attachments[i].type)
|
|
512
|
-
}
|
|
513
|
-
|
|
514
|
-
let blobParts = []
|
|
515
|
-
let boundary =
|
|
516
|
-
(Math.random() + ' ').substring(2, 10) +
|
|
517
|
-
(Math.random() + ' ').substring(2, 10)
|
|
518
|
-
|
|
519
|
-
extraHeaders['Content-Type'] = 'multipart/mixed; boundary=' + boundary
|
|
520
|
-
|
|
521
|
-
let CRLF = '\r\n'
|
|
522
|
-
let header =
|
|
523
|
-
[
|
|
524
|
-
'--' + boundary,
|
|
525
|
-
'Content-Type: application/json',
|
|
526
|
-
'Content-Disposition: form-data; name="statement"',
|
|
527
|
-
'',
|
|
528
|
-
JSON.stringify(statement)
|
|
529
|
-
].join(CRLF) + CRLF
|
|
530
|
-
|
|
531
|
-
blobParts.push(header)
|
|
532
|
-
|
|
533
|
-
for (let i in attachments) {
|
|
534
|
-
if (attachments.hasOwnProperty(i)) {
|
|
535
|
-
let attachmentHeader =
|
|
536
|
-
[
|
|
537
|
-
'--' + boundary,
|
|
538
|
-
'Content-Type: ' + attachments[i].type.contentType,
|
|
539
|
-
'Content-Transfer-Encoding: binary',
|
|
540
|
-
'X-Experience-API-Hash: ' + attachments[i].type.sha2
|
|
541
|
-
].join(CRLF) +
|
|
542
|
-
CRLF +
|
|
543
|
-
CRLF
|
|
544
|
-
|
|
545
|
-
blobParts.push(attachmentHeader)
|
|
546
|
-
blobParts.push(attachments[i].value)
|
|
547
|
-
}
|
|
548
|
-
}
|
|
549
|
-
|
|
550
|
-
blobParts.push(CRLF + '--' + boundary + '--' + CRLF)
|
|
551
|
-
|
|
552
|
-
return new Blob(blobParts)
|
|
553
|
-
}
|
|
554
|
-
/*
|
|
555
|
-
* Send a list of statements to the LRS.
|
|
556
|
-
* @param {array} stmtArray the list of statement objects to send
|
|
557
|
-
* @param {function} [callback] function to be called after the LRS responds
|
|
558
|
-
* to this request (makes the call asynchronous)
|
|
559
|
-
* the function will be passed the XMLHttpRequest object
|
|
560
|
-
* @return {object} xhr response object
|
|
561
|
-
* @example
|
|
562
|
-
* let stmt = {"actor" : {"mbox" : "mailto:tom@example.com"},
|
|
563
|
-
* "verb" : {"id" : "http://adlnet.gov/expapi/verbs/answered",
|
|
564
|
-
* "display" : {"en-US" : "answered"}},
|
|
565
|
-
* "object" : {"id" : "http://adlnet.gov/expapi/activities/question"}};
|
|
566
|
-
* let resp_obj = ADL.XAPIWrapper.sendStatement(stmt);
|
|
567
|
-
* ADL.XAPIWrapper.getStatements({"statementId":resp_obj.id});
|
|
568
|
-
* >> {"version": "1.0.0",
|
|
569
|
-
* "timestamp": "2013-09-09 21:36:40.185841+00:00",
|
|
570
|
-
* "object": {"id": "http://adlnet.gov/expapi/activities/question", "objectType": "Activity"},
|
|
571
|
-
* "actor": {"mbox": "mailto:tom@example.com", "name": "tom creighton", "objectType": "Agent"},
|
|
572
|
-
* "stored": "2013-09-09 21:36:40.186124+00:00",
|
|
573
|
-
* "verb": {"id": "http://adlnet.gov/expapi/verbs/answered", "display": {"en-US": "answered"}},
|
|
574
|
-
* "authority": {"mbox": "mailto:tom@adlnet.gov", "name": "tom", "objectType": "Agent"},
|
|
575
|
-
* "context": {"registration": "51a6f860-1997-11e3-8ffd-0800200c9a66"},
|
|
576
|
-
* "id": "ea9c1d01-0606-4ec7-8e5d-20f87b1211ed"}
|
|
577
|
-
*/
|
|
578
|
-
XAPIWrapper.prototype.sendStatements = function (stmtArray, callback) {
|
|
579
|
-
if (this.testConfig()) {
|
|
580
|
-
for (let i in stmtArray) {
|
|
581
|
-
if (stmtArray.hasOwnProperty(i)) this.prepareStatement(stmtArray[i])
|
|
582
|
-
}
|
|
583
|
-
let resp = ADL.XHR_request(
|
|
584
|
-
this.lrs,
|
|
585
|
-
this.lrs.endpoint + 'statements',
|
|
586
|
-
'POST',
|
|
587
|
-
JSON.stringify(stmtArray),
|
|
588
|
-
this.lrs.auth,
|
|
589
|
-
callback,
|
|
590
|
-
null,
|
|
591
|
-
false,
|
|
592
|
-
null,
|
|
593
|
-
this.withCredentials,
|
|
594
|
-
this.strictCallbacks
|
|
595
|
-
)
|
|
596
|
-
|
|
597
|
-
if (!callback) {
|
|
598
|
-
return resp
|
|
599
|
-
}
|
|
600
|
-
}
|
|
601
|
-
}
|
|
602
|
-
|
|
603
|
-
/*
|
|
604
|
-
* Send a list of statements to the LRS.
|
|
605
|
-
* @param {array} stmtArray the list of statement objects to send
|
|
606
|
-
* @param {function} [callback] function to be called after the LRS responds
|
|
607
|
-
* to this request (makes the call asynchronous)
|
|
608
|
-
* the function will be passed the XMLHttpRequest object
|
|
609
|
-
* @return {object} xhr response object
|
|
610
|
-
* @example
|
|
611
|
-
* let stmt = {"actor" : {"mbox" : "mailto:tom@example.com"},
|
|
612
|
-
* "verb" : {"id" : "http://adlnet.gov/expapi/verbs/answered",
|
|
613
|
-
* "display" : {"en-US" : "answered"}},
|
|
614
|
-
* "object" : {"id" : "http://adlnet.gov/expapi/activities/question"}};
|
|
615
|
-
* let resp_obj = ADL.XAPIWrapper.sendStatement(stmt);
|
|
616
|
-
* ADL.XAPIWrapper.getStatements({"statementId":resp_obj.id});
|
|
617
|
-
* >> {"version": "1.0.0",
|
|
618
|
-
* "timestamp": "2013-09-09 21:36:40.185841+00:00",
|
|
619
|
-
* "object": {"id": "http://adlnet.gov/expapi/activities/question", "objectType": "Activity"},
|
|
620
|
-
* "actor": {"mbox": "mailto:tom@example.com", "name": "tom creighton", "objectType": "Agent"},
|
|
621
|
-
* "stored": "2013-09-09 21:36:40.186124+00:00",
|
|
622
|
-
* "verb": {"id": "http://adlnet.gov/expapi/verbs/answered", "display": {"en-US": "answered"}},
|
|
623
|
-
* "authority": {"mbox": "mailto:tom@adlnet.gov", "name": "tom", "objectType": "Agent"},
|
|
624
|
-
* "context": {"registration": "51a6f860-1997-11e3-8ffd-0800200c9a66"},
|
|
625
|
-
* "id": "ea9c1d01-0606-4ec7-8e5d-20f87b1211ed"}
|
|
626
|
-
*/
|
|
627
|
-
XAPIWrapper.prototype.sendStatementsWithFetch = async function (
|
|
628
|
-
stmtArray,
|
|
629
|
-
callback
|
|
630
|
-
) {
|
|
631
|
-
if (this.testConfig()) {
|
|
632
|
-
for (let i in stmtArray) {
|
|
633
|
-
if (stmtArray.hasOwnProperty(i)) this.prepareStatement(stmtArray[i])
|
|
634
|
-
}
|
|
635
|
-
// let resp = ADL.XHR_request(
|
|
636
|
-
// this.lrs,
|
|
637
|
-
// this.lrs.endpoint + 'statements',
|
|
638
|
-
// 'POST',
|
|
639
|
-
// JSON.stringify(stmtArray),
|
|
640
|
-
// this.lrs.auth,
|
|
641
|
-
// callback,
|
|
642
|
-
// null,
|
|
643
|
-
// false,
|
|
644
|
-
// null,
|
|
645
|
-
// this.withCredentials,
|
|
646
|
-
// this.strictCallbacks
|
|
647
|
-
// )
|
|
648
|
-
//=============================================
|
|
649
|
-
let headers = {}
|
|
650
|
-
headers['Content-type'] = 'application/json; charset=UTF-8'
|
|
651
|
-
headers['Authorization'] = this.lrs.auth
|
|
652
|
-
headers['X-Experience-API-Version'] = ADL.XAPIWrapper.xapiVersion
|
|
653
|
-
|
|
654
|
-
let payload = JSON.stringify(stmtArray)
|
|
655
|
-
// fecth API used to allow the delivery of the request after the browser is closed
|
|
656
|
-
let resp = await fetch(this.lrs.endpoint + 'statements', {
|
|
657
|
-
method: 'POST',
|
|
658
|
-
headers,
|
|
659
|
-
body: payload,
|
|
660
|
-
keepalive: true // allow the request to outlive the closing of browser tab
|
|
661
|
-
})
|
|
662
|
-
|
|
663
|
-
//=============================================
|
|
664
|
-
if (!callback) {
|
|
665
|
-
return resp
|
|
666
|
-
}
|
|
667
|
-
}
|
|
668
|
-
}
|
|
669
|
-
|
|
670
|
-
/*
|
|
671
|
-
* Get statement(s) based on the searchparams or more url.
|
|
672
|
-
* @param {object} searchparams an ADL.XAPIWrapper.searchParams object of
|
|
673
|
-
* key(search parameter)-value(parameter value) pairs.
|
|
674
|
-
* Example:
|
|
675
|
-
* let myparams = ADL.XAPIWrapper.searchParams();
|
|
676
|
-
* myparams['verb'] = ADL.verbs.completed.id;
|
|
677
|
-
* let completedStmts = ADL.XAPIWrapper.getStatements(myparams);
|
|
678
|
-
* @param {string} more the more url found in the StatementResults object, if there are more
|
|
679
|
-
* statements available based on your get statements request. Pass the
|
|
680
|
-
* more url as this parameter to retrieve those statements.
|
|
681
|
-
* @param {function} [callback] - function to be called after the LRS responds
|
|
682
|
-
* to this request (makes the call asynchronous)
|
|
683
|
-
* the function will be passed the XMLHttpRequest object
|
|
684
|
-
* @return {object} xhr response object or null if 404
|
|
685
|
-
* @example
|
|
686
|
-
* let ret = ADL.XAPIWrapper.getStatements();
|
|
687
|
-
* if (ret)
|
|
688
|
-
* ADL.XAPIWrapper.log(ret.statements);
|
|
689
|
-
*
|
|
690
|
-
* >> <Array of statements>
|
|
691
|
-
*/
|
|
692
|
-
XAPIWrapper.prototype.getStatements = async function (
|
|
693
|
-
searchparams,
|
|
694
|
-
more,
|
|
695
|
-
callback
|
|
696
|
-
) {
|
|
697
|
-
if (this.testConfig()) {
|
|
698
|
-
let url = this.lrs.endpoint + 'statements'
|
|
699
|
-
if (more) {
|
|
700
|
-
url = this.base + more
|
|
701
|
-
} else {
|
|
702
|
-
let urlparams = new Array()
|
|
703
|
-
|
|
704
|
-
for (let s in searchparams) {
|
|
705
|
-
if (searchparams.hasOwnProperty(s)) {
|
|
706
|
-
if (s == 'until' || s == 'since') {
|
|
707
|
-
let d = new Date(searchparams[s])
|
|
708
|
-
urlparams.push(s + '=' + encodeURIComponent(d.toISOString()))
|
|
709
|
-
} else {
|
|
710
|
-
urlparams.push(s + '=' + encodeURIComponent(searchparams[s]))
|
|
711
|
-
}
|
|
712
|
-
}
|
|
713
|
-
}
|
|
714
|
-
if (urlparams.length > 0) url = url + '?' + urlparams.join('&')
|
|
715
|
-
}
|
|
716
|
-
let res = await ADL.XHR_request(
|
|
717
|
-
this.lrs,
|
|
718
|
-
url,
|
|
719
|
-
'GET',
|
|
720
|
-
null,
|
|
721
|
-
this.lrs.auth,
|
|
722
|
-
callback,
|
|
723
|
-
null,
|
|
724
|
-
false,
|
|
725
|
-
null,
|
|
726
|
-
this.withCredentials,
|
|
727
|
-
this.strictCallbacks
|
|
728
|
-
)
|
|
729
|
-
|
|
730
|
-
if (res === undefined || res.status == 404) {
|
|
731
|
-
return null
|
|
732
|
-
}
|
|
733
|
-
|
|
734
|
-
try {
|
|
735
|
-
return JSON.parse(res.response)
|
|
736
|
-
} catch (e) {
|
|
737
|
-
return res.response
|
|
738
|
-
}
|
|
739
|
-
}
|
|
740
|
-
}
|
|
741
|
-
|
|
742
|
-
/*
|
|
743
|
-
* Gets the Activity object from the LRS.
|
|
744
|
-
* @param {string} activityid the id of the Activity to get
|
|
745
|
-
* @param {function} [callback] function to be called after the LRS responds
|
|
746
|
-
* to this request (makes the call asynchronous)
|
|
747
|
-
* the function will be passed the XMLHttpRequest object
|
|
748
|
-
* @return {object} xhr response object or null if 404
|
|
749
|
-
* @example
|
|
750
|
-
* let res = ADL.XAPIWrapper.getActivities("http://adlnet.gov/expapi/activities/question");
|
|
751
|
-
* ADL.XAPIWrapper.log(res);
|
|
752
|
-
* >> <Activity object>
|
|
753
|
-
*/
|
|
754
|
-
XAPIWrapper.prototype.getActivities = function (activityid, callback) {
|
|
755
|
-
if (this.testConfig()) {
|
|
756
|
-
let url = this.lrs.endpoint + 'activities?activityId=<activityid>'
|
|
757
|
-
url = url.replace('<activityid>', encodeURIComponent(activityid))
|
|
758
|
-
|
|
759
|
-
let result = ADL.XHR_request(
|
|
760
|
-
this.lrs,
|
|
761
|
-
url,
|
|
762
|
-
'GET',
|
|
763
|
-
null,
|
|
764
|
-
this.lrs.auth,
|
|
765
|
-
callback,
|
|
766
|
-
null,
|
|
767
|
-
true,
|
|
768
|
-
null,
|
|
769
|
-
this.withCredentials,
|
|
770
|
-
this.strictCallbacks
|
|
771
|
-
)
|
|
772
|
-
|
|
773
|
-
if (result === undefined || result.status == 404) {
|
|
774
|
-
return null
|
|
775
|
-
}
|
|
776
|
-
|
|
777
|
-
try {
|
|
778
|
-
return JSON.parse(result.response)
|
|
779
|
-
} catch (e) {
|
|
780
|
-
return result.response
|
|
781
|
-
}
|
|
782
|
-
}
|
|
783
|
-
}
|
|
784
|
-
|
|
785
|
-
/*
|
|
786
|
-
* Store activity state in the LRS
|
|
787
|
-
* @param {string} activityid the id of the Activity this state is about
|
|
788
|
-
* @param {object} agent the agent this Activity state is related to
|
|
789
|
-
* @param {string} stateid the id you want associated with this state
|
|
790
|
-
* @param {string} [registration] the registraton id associated with this state
|
|
791
|
-
* @param {string} stateval the state
|
|
792
|
-
* @param {string} [matchHash] the hash of the state to replace or * to replace any
|
|
793
|
-
* @param {string} [noneMatchHash] the hash of the current state or * to indicate no previous state
|
|
794
|
-
* @param {function} [callback] function to be called after the LRS responds
|
|
795
|
-
* to this request (makes the call asynchronous)
|
|
796
|
-
* the function will be passed the XMLHttpRequest object
|
|
797
|
-
* @return {boolean} false if no activity state is included
|
|
798
|
-
* @example
|
|
799
|
-
* let stateval = {"info":"the state info"};
|
|
800
|
-
* ADL.XAPIWrapper.sendState("http://adlnet.gov/expapi/activities/question",
|
|
801
|
-
* {"mbox":"mailto:tom@example.com"},
|
|
802
|
-
* "questionstate", null, stateval);
|
|
803
|
-
*/
|
|
804
|
-
XAPIWrapper.prototype.sendState = function (
|
|
805
|
-
activityid,
|
|
806
|
-
agent,
|
|
807
|
-
stateid,
|
|
808
|
-
registration,
|
|
809
|
-
stateval,
|
|
810
|
-
matchHash,
|
|
811
|
-
noneMatchHash,
|
|
812
|
-
callback
|
|
813
|
-
) {
|
|
814
|
-
if (this.testConfig()) {
|
|
815
|
-
let url =
|
|
816
|
-
this.lrs.endpoint +
|
|
817
|
-
'activities/state?activityId=<activity ID>&agent=<agent>&stateId=<stateid>'
|
|
818
|
-
|
|
819
|
-
url = url.replace('<activity ID>', encodeURIComponent(activityid))
|
|
820
|
-
url = url.replace('<agent>', encodeURIComponent(JSON.stringify(agent)))
|
|
821
|
-
url = url.replace('<stateid>', encodeURIComponent(stateid))
|
|
822
|
-
|
|
823
|
-
if (registration) {
|
|
824
|
-
url += '®istration=' + encodeURIComponent(registration)
|
|
825
|
-
}
|
|
826
|
-
|
|
827
|
-
let headers = null
|
|
828
|
-
if (matchHash && noneMatchHash) {
|
|
829
|
-
log("Can't have both If-Match and If-None-Match")
|
|
830
|
-
} else if (matchHash) {
|
|
831
|
-
headers = { 'If-Match': ADL.formatHash(matchHash) }
|
|
832
|
-
} else if (noneMatchHash) {
|
|
833
|
-
headers = { 'If-None-Match': ADL.formatHash(noneMatchHash) }
|
|
834
|
-
}
|
|
835
|
-
|
|
836
|
-
let method = 'PUT'
|
|
837
|
-
if (stateval) {
|
|
838
|
-
if (stateval instanceof Array) {
|
|
839
|
-
stateval = JSON.stringify(stateval)
|
|
840
|
-
headers = headers || {}
|
|
841
|
-
headers['Content-Type'] = 'application/json'
|
|
842
|
-
} else if (stateval instanceof Object) {
|
|
843
|
-
stateval = JSON.stringify(stateval)
|
|
844
|
-
headers = headers || {}
|
|
845
|
-
headers['Content-Type'] = 'application/json'
|
|
846
|
-
method = 'POST'
|
|
847
|
-
} else {
|
|
848
|
-
headers = headers || {}
|
|
849
|
-
headers['Content-Type'] = 'application/octet-stream'
|
|
850
|
-
}
|
|
851
|
-
} else {
|
|
852
|
-
this.log('No activity state was included.')
|
|
853
|
-
return false
|
|
854
|
-
}
|
|
855
|
-
//(lrs, url, method, data, auth, callback, callbackargs, ignore404, extraHeaders)
|
|
856
|
-
|
|
857
|
-
ADL.XHR_request(
|
|
858
|
-
this.lrs,
|
|
859
|
-
url,
|
|
860
|
-
method,
|
|
861
|
-
stateval,
|
|
862
|
-
this.lrs.auth,
|
|
863
|
-
callback,
|
|
864
|
-
null,
|
|
865
|
-
null,
|
|
866
|
-
headers,
|
|
867
|
-
this.withCredentials,
|
|
868
|
-
this.strictCallbacks
|
|
869
|
-
)
|
|
870
|
-
}
|
|
871
|
-
}
|
|
872
|
-
|
|
873
|
-
/*
|
|
874
|
-
* Get activity state from the LRS
|
|
875
|
-
* @param {string} activityid the id of the Activity this state is about
|
|
876
|
-
* @param {object} agent the agent this Activity state is related to
|
|
877
|
-
* @param {string} [stateid] the id of the state, if not included, the response will be a list of stateids
|
|
878
|
-
* associated with the activity and agent)
|
|
879
|
-
* @param {string} [registration] the registraton id associated with this state
|
|
880
|
-
* @param {object} [since] date object or date string telling the LRS to return objects newer than the date supplied
|
|
881
|
-
* @param {function} [callback] function to be called after the LRS responds
|
|
882
|
-
* to this request (makes the call asynchronous)
|
|
883
|
-
* the function will be passed the XMLHttpRequest object
|
|
884
|
-
* @return {object} xhr response object or null if 404
|
|
885
|
-
* @example
|
|
886
|
-
* ADL.XAPIWrapper.getState("http://adlnet.gov/expapi/activities/question",
|
|
887
|
-
* {"mbox":"mailto:tom@example.com"}, "questionstate");
|
|
888
|
-
* >> {info: "the state info"}
|
|
889
|
-
*/
|
|
890
|
-
XAPIWrapper.prototype.getState = function (
|
|
891
|
-
activityid,
|
|
892
|
-
agent,
|
|
893
|
-
stateid,
|
|
894
|
-
registration,
|
|
895
|
-
since,
|
|
896
|
-
callback
|
|
897
|
-
) {
|
|
898
|
-
if (this.testConfig()) {
|
|
899
|
-
let url =
|
|
900
|
-
this.lrs.endpoint +
|
|
901
|
-
'activities/state?activityId=<activity ID>&agent=<agent>'
|
|
902
|
-
|
|
903
|
-
url = url.replace('<activity ID>', encodeURIComponent(activityid))
|
|
904
|
-
url = url.replace('<agent>', encodeURIComponent(JSON.stringify(agent)))
|
|
905
|
-
|
|
906
|
-
if (stateid) {
|
|
907
|
-
url += '&stateId=' + encodeURIComponent(stateid)
|
|
908
|
-
}
|
|
909
|
-
|
|
910
|
-
if (registration) {
|
|
911
|
-
url += '®istration=' + encodeURIComponent(registration)
|
|
912
|
-
}
|
|
913
|
-
|
|
914
|
-
if (since) {
|
|
915
|
-
since = isDate(since)
|
|
916
|
-
if (since != null) {
|
|
917
|
-
url += '&since=' + encodeURIComponent(since.toISOString())
|
|
918
|
-
}
|
|
919
|
-
}
|
|
920
|
-
|
|
921
|
-
let result = ADL.XHR_request(
|
|
922
|
-
this.lrs,
|
|
923
|
-
url,
|
|
924
|
-
'GET',
|
|
925
|
-
null,
|
|
926
|
-
this.lrs.auth,
|
|
927
|
-
callback,
|
|
928
|
-
null,
|
|
929
|
-
true,
|
|
930
|
-
null,
|
|
931
|
-
this.withCredentials,
|
|
932
|
-
this.strictCallbacks
|
|
933
|
-
)
|
|
934
|
-
|
|
935
|
-
if (result === undefined || result.status == 404) {
|
|
936
|
-
return null
|
|
937
|
-
}
|
|
938
|
-
|
|
939
|
-
try {
|
|
940
|
-
return JSON.parse(result.response)
|
|
941
|
-
} catch (e) {
|
|
942
|
-
return result.response
|
|
943
|
-
}
|
|
944
|
-
}
|
|
945
|
-
}
|
|
946
|
-
|
|
947
|
-
/*
|
|
948
|
-
* Delete activity state in the LRS
|
|
949
|
-
* @param {string} activityid the id of the Activity this state is about
|
|
950
|
-
* @param {object} agent the agent this Activity state is related to
|
|
951
|
-
* @param {string} stateid the id you want associated with this state
|
|
952
|
-
* @param {string} [registration] the registraton id associated with this state
|
|
953
|
-
* @param {string} [matchHash] the hash of the state to replace or * to replace any
|
|
954
|
-
* @param {string} [noneMatchHash] the hash of the current state or * to indicate no previous state
|
|
955
|
-
* @param {string} [callback] function to be called after the LRS responds
|
|
956
|
-
* to this request (makes the call asynchronous)
|
|
957
|
-
* the function will be passed the XMLHttpRequest object
|
|
958
|
-
* @return {object} xhr response object or null if 404
|
|
959
|
-
* @example
|
|
960
|
-
* let stateval = {"info":"the state info"};
|
|
961
|
-
* ADL.XAPIWrapper.sendState("http://adlnet.gov/expapi/activities/question",
|
|
962
|
-
* {"mbox":"mailto:tom@example.com"},
|
|
963
|
-
* "questionstate", null, stateval);
|
|
964
|
-
* ADL.XAPIWrapper.getState("http://adlnet.gov/expapi/activities/question",
|
|
965
|
-
* {"mbox":"mailto:tom@example.com"}, "questionstate");
|
|
966
|
-
* >> {info: "the state info"}
|
|
967
|
-
*
|
|
968
|
-
* ADL.XAPIWrapper.deleteState("http://adlnet.gov/expapi/activities/question",
|
|
969
|
-
* {"mbox":"mailto:tom@example.com"}, "questionstate");
|
|
970
|
-
* >> XMLHttpRequest {statusText: "NO CONTENT", status: 204, response: "", responseType: "", responseXML: null…}
|
|
971
|
-
*
|
|
972
|
-
* ADL.XAPIWrapper.getState("http://adlnet.gov/expapi/activities/question",
|
|
973
|
-
* {"mbox":"mailto:tom@example.com"}, "questionstate");
|
|
974
|
-
* >> 404
|
|
975
|
-
*/
|
|
976
|
-
XAPIWrapper.prototype.deleteState = function (
|
|
977
|
-
activityid,
|
|
978
|
-
agent,
|
|
979
|
-
stateid,
|
|
980
|
-
registration,
|
|
981
|
-
matchHash,
|
|
982
|
-
noneMatchHash,
|
|
983
|
-
callback
|
|
984
|
-
) {
|
|
985
|
-
if (this.testConfig()) {
|
|
986
|
-
let url =
|
|
987
|
-
this.lrs.endpoint +
|
|
988
|
-
'activities/state?activityId=<activity ID>&agent=<agent>&stateId=<stateid>'
|
|
989
|
-
|
|
990
|
-
url = url.replace('<activity ID>', encodeURIComponent(activityid))
|
|
991
|
-
url = url.replace('<agent>', encodeURIComponent(JSON.stringify(agent)))
|
|
992
|
-
url = url.replace('<stateid>', encodeURIComponent(stateid))
|
|
993
|
-
|
|
994
|
-
if (registration) {
|
|
995
|
-
url += '®istration=' + encodeURIComponent(registration)
|
|
996
|
-
}
|
|
997
|
-
|
|
998
|
-
let headers = null
|
|
999
|
-
if (matchHash && noneMatchHash) {
|
|
1000
|
-
log("Can't have both If-Match and If-None-Match")
|
|
1001
|
-
} else if (matchHash) {
|
|
1002
|
-
headers = { 'If-Match': ADL.formatHash(matchHash) }
|
|
1003
|
-
} else if (noneMatchHash) {
|
|
1004
|
-
headers = { 'If-None-Match': ADL.formatHash(noneMatchHash) }
|
|
1005
|
-
}
|
|
1006
|
-
|
|
1007
|
-
let result = ADL.XHR_request(
|
|
1008
|
-
this.lrs,
|
|
1009
|
-
url,
|
|
1010
|
-
'DELETE',
|
|
1011
|
-
null,
|
|
1012
|
-
this.lrs.auth,
|
|
1013
|
-
callback,
|
|
1014
|
-
null,
|
|
1015
|
-
false,
|
|
1016
|
-
headers,
|
|
1017
|
-
this.withCredentials,
|
|
1018
|
-
this.strictCallbacks
|
|
1019
|
-
)
|
|
1020
|
-
|
|
1021
|
-
if (result === undefined || result.status == 404) {
|
|
1022
|
-
return null
|
|
1023
|
-
}
|
|
1024
|
-
|
|
1025
|
-
try {
|
|
1026
|
-
return JSON.parse(result.response)
|
|
1027
|
-
} catch (e) {
|
|
1028
|
-
return result
|
|
1029
|
-
}
|
|
1030
|
-
}
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
/*
|
|
1034
|
-
* Store activity profile in the LRS
|
|
1035
|
-
* @param {string} activityid the id of the Activity this profile is about
|
|
1036
|
-
* @param {string} profileid the id you want associated with this profile
|
|
1037
|
-
* @param {string} profileval the profile
|
|
1038
|
-
* @param {string} [matchHash] the hash of the profile to replace or * to replace any
|
|
1039
|
-
* @param {string} [noneMatchHash] the hash of the current profile or * to indicate no previous profile
|
|
1040
|
-
* @param {string} [callback] function to be called after the LRS responds
|
|
1041
|
-
* to this request (makes the call asynchronous)
|
|
1042
|
-
* the function will be passed the XMLHttpRequest object
|
|
1043
|
-
* @return {bolean} false if no activity profile is included
|
|
1044
|
-
* @example
|
|
1045
|
-
* let profile = {"info":"the profile"};
|
|
1046
|
-
* ADL.XAPIWrapper.sendActivityProfile("http://adlnet.gov/expapi/activities/question",
|
|
1047
|
-
* "actprofile", profile, null, "*");
|
|
1048
|
-
*/
|
|
1049
|
-
XAPIWrapper.prototype.sendActivityProfile = function (
|
|
1050
|
-
activityid,
|
|
1051
|
-
profileid,
|
|
1052
|
-
profileval,
|
|
1053
|
-
matchHash,
|
|
1054
|
-
noneMatchHash,
|
|
1055
|
-
callback
|
|
1056
|
-
) {
|
|
1057
|
-
if (this.testConfig()) {
|
|
1058
|
-
let url =
|
|
1059
|
-
this.lrs.endpoint +
|
|
1060
|
-
'activities/profile?activityId=<activity ID>&profileId=<profileid>'
|
|
1061
|
-
|
|
1062
|
-
url = url.replace('<activity ID>', encodeURIComponent(activityid))
|
|
1063
|
-
url = url.replace('<profileid>', encodeURIComponent(profileid))
|
|
1064
|
-
|
|
1065
|
-
let headers = null
|
|
1066
|
-
if (matchHash && noneMatchHash) {
|
|
1067
|
-
log("Can't have both If-Match and If-None-Match")
|
|
1068
|
-
} else if (matchHash) {
|
|
1069
|
-
headers = { 'If-Match': ADL.formatHash(matchHash) }
|
|
1070
|
-
} else if (noneMatchHash) {
|
|
1071
|
-
headers = { 'If-None-Match': ADL.formatHash(noneMatchHash) }
|
|
1072
|
-
}
|
|
1073
|
-
|
|
1074
|
-
let method = 'PUT'
|
|
1075
|
-
if (profileval) {
|
|
1076
|
-
if (profileval instanceof Array) {
|
|
1077
|
-
profileval = JSON.stringify(profileval)
|
|
1078
|
-
headers = headers || {}
|
|
1079
|
-
headers['Content-Type'] = 'application/json'
|
|
1080
|
-
} else if (profileval instanceof Object) {
|
|
1081
|
-
profileval = JSON.stringify(profileval)
|
|
1082
|
-
headers = headers || {}
|
|
1083
|
-
headers['Content-Type'] = 'application/json'
|
|
1084
|
-
method = 'POST'
|
|
1085
|
-
} else {
|
|
1086
|
-
headers = headers || {}
|
|
1087
|
-
headers['Content-Type'] = 'application/octet-stream'
|
|
1088
|
-
}
|
|
1089
|
-
} else {
|
|
1090
|
-
this.log('No activity profile was included.')
|
|
1091
|
-
return false
|
|
1092
|
-
}
|
|
1093
|
-
|
|
1094
|
-
ADL.XHR_request(
|
|
1095
|
-
this.lrs,
|
|
1096
|
-
url,
|
|
1097
|
-
method,
|
|
1098
|
-
profileval,
|
|
1099
|
-
this.lrs.auth,
|
|
1100
|
-
callback,
|
|
1101
|
-
null,
|
|
1102
|
-
false,
|
|
1103
|
-
headers,
|
|
1104
|
-
this.withCredentials,
|
|
1105
|
-
this.strictCallbacks
|
|
1106
|
-
)
|
|
1107
|
-
}
|
|
1108
|
-
}
|
|
1109
|
-
|
|
1110
|
-
/*
|
|
1111
|
-
* Get activity profile from the LRS
|
|
1112
|
-
* @param {string} activityid the id of the Activity this profile is about
|
|
1113
|
-
* @param {string} [profileid] the id of the profile, if not included, the response will be a list of profileids
|
|
1114
|
-
* associated with the activity
|
|
1115
|
-
* @param {object} [since] date object or date string telling the LRS to return objects newer than the date supplied
|
|
1116
|
-
* @param {function [callback] function to be called after the LRS responds
|
|
1117
|
-
* to this request (makes the call asynchronous)
|
|
1118
|
-
* the function will be passed the XMLHttpRequest object
|
|
1119
|
-
* @return {object} xhr response object or null if 404
|
|
1120
|
-
* @example
|
|
1121
|
-
* ADL.XAPIWrapper.getActivityProfile("http://adlnet.gov/expapi/activities/question",
|
|
1122
|
-
* "actprofile", null,
|
|
1123
|
-
* function(r){ADL.XAPIWrapper.log(JSON.parse(r.response));});
|
|
1124
|
-
* >> {info: "the profile"}
|
|
1125
|
-
*/
|
|
1126
|
-
XAPIWrapper.prototype.getActivityProfile = function (
|
|
1127
|
-
activityid,
|
|
1128
|
-
profileid,
|
|
1129
|
-
since,
|
|
1130
|
-
callback
|
|
1131
|
-
) {
|
|
1132
|
-
if (this.testConfig()) {
|
|
1133
|
-
let url =
|
|
1134
|
-
this.lrs.endpoint + 'activities/profile?activityId=<activity ID>'
|
|
1135
|
-
|
|
1136
|
-
url = url.replace('<activity ID>', encodeURIComponent(activityid))
|
|
1137
|
-
|
|
1138
|
-
if (profileid) {
|
|
1139
|
-
url += '&profileId=' + encodeURIComponent(profileid)
|
|
1140
|
-
}
|
|
1141
|
-
|
|
1142
|
-
if (since) {
|
|
1143
|
-
since = isDate(since)
|
|
1144
|
-
if (since != null) {
|
|
1145
|
-
url += '&since=' + encodeURIComponent(since.toISOString())
|
|
1146
|
-
}
|
|
1147
|
-
}
|
|
1148
|
-
|
|
1149
|
-
let result = ADL.XHR_request(
|
|
1150
|
-
this.lrs,
|
|
1151
|
-
url,
|
|
1152
|
-
'GET',
|
|
1153
|
-
null,
|
|
1154
|
-
this.lrs.auth,
|
|
1155
|
-
callback,
|
|
1156
|
-
null,
|
|
1157
|
-
true,
|
|
1158
|
-
null,
|
|
1159
|
-
this.withCredentials,
|
|
1160
|
-
this.strictCallbacks
|
|
1161
|
-
)
|
|
1162
|
-
|
|
1163
|
-
if (result === undefined || result.status == 404) {
|
|
1164
|
-
return null
|
|
1165
|
-
}
|
|
1166
|
-
|
|
1167
|
-
try {
|
|
1168
|
-
return JSON.parse(result.response)
|
|
1169
|
-
} catch (e) {
|
|
1170
|
-
return result.response
|
|
1171
|
-
}
|
|
1172
|
-
}
|
|
1173
|
-
}
|
|
1174
|
-
|
|
1175
|
-
/*
|
|
1176
|
-
* Delete activity profile in the LRS
|
|
1177
|
-
* @param {string} activityid the id of the Activity this profile is about
|
|
1178
|
-
* @param {string} profileid the id you want associated with this profile
|
|
1179
|
-
* @param {string} [matchHash] the hash of the profile to replace or * to replace any
|
|
1180
|
-
* @param {string} [noneMatchHash] the hash of the current profile or * to indicate no previous profile
|
|
1181
|
-
* @param {string} [callback] function to be called after the LRS responds
|
|
1182
|
-
* to this request (makes the call asynchronous)
|
|
1183
|
-
* the function will be passed the XMLHttpRequest object
|
|
1184
|
-
* @return {object} xhr response object or null if 404
|
|
1185
|
-
* @example
|
|
1186
|
-
* ADL.XAPIWrapper.deleteActivityProfile("http://adlnet.gov/expapi/activities/question",
|
|
1187
|
-
* "actprofile");
|
|
1188
|
-
* >> XMLHttpRequest {statusText: "NO CONTENT", status: 204, response: "", responseType: "", responseXML: null…}
|
|
1189
|
-
*/
|
|
1190
|
-
XAPIWrapper.prototype.deleteActivityProfile = function (
|
|
1191
|
-
activityid,
|
|
1192
|
-
profileid,
|
|
1193
|
-
matchHash,
|
|
1194
|
-
noneMatchHash,
|
|
1195
|
-
callback
|
|
1196
|
-
) {
|
|
1197
|
-
if (this.testConfig()) {
|
|
1198
|
-
let url =
|
|
1199
|
-
this.lrs.endpoint +
|
|
1200
|
-
'activities/profile?activityId=<activity ID>&profileId=<profileid>'
|
|
1201
|
-
|
|
1202
|
-
url = url.replace('<activity ID>', encodeURIComponent(activityid))
|
|
1203
|
-
url = url.replace('<profileid>', encodeURIComponent(profileid))
|
|
1204
|
-
|
|
1205
|
-
let headers = null
|
|
1206
|
-
if (matchHash && noneMatchHash) {
|
|
1207
|
-
log("Can't have both If-Match and If-None-Match")
|
|
1208
|
-
} else if (matchHash) {
|
|
1209
|
-
headers = { 'If-Match': ADL.formatHash(matchHash) }
|
|
1210
|
-
} else if (noneMatchHash) {
|
|
1211
|
-
headers = { 'If-None-Match': ADL.formatHash(noneMatchHash) }
|
|
1212
|
-
}
|
|
1213
|
-
|
|
1214
|
-
let result = ADL.XHR_request(
|
|
1215
|
-
this.lrs,
|
|
1216
|
-
url,
|
|
1217
|
-
'DELETE',
|
|
1218
|
-
null,
|
|
1219
|
-
this.lrs.auth,
|
|
1220
|
-
callback,
|
|
1221
|
-
null,
|
|
1222
|
-
false,
|
|
1223
|
-
headers,
|
|
1224
|
-
this.withCredentials,
|
|
1225
|
-
this.strictCallbacks
|
|
1226
|
-
)
|
|
1227
|
-
|
|
1228
|
-
if (result === undefined || result.status == 404) {
|
|
1229
|
-
return null
|
|
1230
|
-
}
|
|
1231
|
-
|
|
1232
|
-
try {
|
|
1233
|
-
return JSON.parse(result.response)
|
|
1234
|
-
} catch (e) {
|
|
1235
|
-
return result
|
|
1236
|
-
}
|
|
1237
|
-
}
|
|
1238
|
-
}
|
|
1239
|
-
|
|
1240
|
-
/*
|
|
1241
|
-
* Gets the Person object from the LRS based on an agent object.
|
|
1242
|
-
* The Person object may contain more information about an agent.
|
|
1243
|
-
* See the xAPI Spec for details.
|
|
1244
|
-
* @param {object} agent the agent object to get a Person
|
|
1245
|
-
* @param {function [callback] function to be called after the LRS responds
|
|
1246
|
-
* to this request (makes the call asynchronous)
|
|
1247
|
-
* the function will be passed the XMLHttpRequest object
|
|
1248
|
-
* @return {object} xhr response object or null if 404
|
|
1249
|
-
* @example
|
|
1250
|
-
* let res = ADL.XAPIWrapper.getAgents({"mbox":"mailto:tom@example.com"});
|
|
1251
|
-
* ADL.XAPIWrapper.log(res);
|
|
1252
|
-
* >> <Person object>
|
|
1253
|
-
*/
|
|
1254
|
-
XAPIWrapper.prototype.getAgents = function (agent, callback) {
|
|
1255
|
-
if (this.testConfig()) {
|
|
1256
|
-
let url = this.lrs.endpoint + 'agents?agent=<agent>'
|
|
1257
|
-
url = url.replace('<agent>', encodeURIComponent(JSON.stringify(agent)))
|
|
1258
|
-
|
|
1259
|
-
let result = ADL.XHR_request(
|
|
1260
|
-
this.lrs,
|
|
1261
|
-
url,
|
|
1262
|
-
'GET',
|
|
1263
|
-
null,
|
|
1264
|
-
this.lrs.auth,
|
|
1265
|
-
callback,
|
|
1266
|
-
null,
|
|
1267
|
-
true,
|
|
1268
|
-
null,
|
|
1269
|
-
this.withCredentials,
|
|
1270
|
-
this.strictCallbacks
|
|
1271
|
-
)
|
|
1272
|
-
|
|
1273
|
-
if (result === undefined || result.status == 404) {
|
|
1274
|
-
return null
|
|
1275
|
-
}
|
|
1276
|
-
|
|
1277
|
-
try {
|
|
1278
|
-
return JSON.parse(result.response)
|
|
1279
|
-
} catch (e) {
|
|
1280
|
-
return result.response
|
|
1281
|
-
}
|
|
1282
|
-
}
|
|
1283
|
-
}
|
|
1284
|
-
|
|
1285
|
-
/*
|
|
1286
|
-
* Store agent profile in the LRS
|
|
1287
|
-
* @param {object} agent the agent this profile is related to
|
|
1288
|
-
* @param {string} profileid the id you want associated with this profile
|
|
1289
|
-
* @param {string} profileval the profile
|
|
1290
|
-
* @param {string} [matchHash] the hash of the profile to replace or * to replace any
|
|
1291
|
-
* @param {string} [noneMatchHash] the hash of the current profile or * to indicate no previous profile
|
|
1292
|
-
* @param {string} [callback] function to be called after the LRS responds
|
|
1293
|
-
* to this request (makes the call asynchronous)
|
|
1294
|
-
* the function will be passed the XMLHttpRequest object
|
|
1295
|
-
* @return {object} false if no agent profile is included
|
|
1296
|
-
* @example
|
|
1297
|
-
* let profile = {"info":"the agent profile"};
|
|
1298
|
-
* ADL.XAPIWrapper.sendAgentProfile({"mbox":"mailto:tom@example.com"},
|
|
1299
|
-
* "agentprofile", profile, null, "*");
|
|
1300
|
-
*/
|
|
1301
|
-
XAPIWrapper.prototype.sendAgentProfile = function (
|
|
1302
|
-
agent,
|
|
1303
|
-
profileid,
|
|
1304
|
-
profileval,
|
|
1305
|
-
matchHash,
|
|
1306
|
-
noneMatchHash,
|
|
1307
|
-
callback
|
|
1308
|
-
) {
|
|
1309
|
-
if (this.testConfig()) {
|
|
1310
|
-
let url =
|
|
1311
|
-
this.lrs.endpoint + 'agents/profile?agent=<agent>&profileId=<profileid>'
|
|
1312
|
-
|
|
1313
|
-
url = url.replace('<agent>', encodeURIComponent(JSON.stringify(agent)))
|
|
1314
|
-
url = url.replace('<profileid>', encodeURIComponent(profileid))
|
|
1315
|
-
|
|
1316
|
-
let headers = null
|
|
1317
|
-
if (matchHash && noneMatchHash) {
|
|
1318
|
-
log("Can't have both If-Match and If-None-Match")
|
|
1319
|
-
} else if (matchHash) {
|
|
1320
|
-
headers = { 'If-Match': ADL.formatHash(matchHash) }
|
|
1321
|
-
} else if (noneMatchHash) {
|
|
1322
|
-
headers = { 'If-None-Match': ADL.formatHash(noneMatchHash) }
|
|
1323
|
-
}
|
|
1324
|
-
|
|
1325
|
-
let method = 'PUT'
|
|
1326
|
-
if (profileval) {
|
|
1327
|
-
if (profileval instanceof Array) {
|
|
1328
|
-
profileval = JSON.stringify(profileval)
|
|
1329
|
-
headers = headers || {}
|
|
1330
|
-
headers['Content-Type'] = 'application/json'
|
|
1331
|
-
} else if (profileval instanceof Object) {
|
|
1332
|
-
profileval = JSON.stringify(profileval)
|
|
1333
|
-
headers = headers || {}
|
|
1334
|
-
headers['Content-Type'] = 'application/json'
|
|
1335
|
-
method = 'POST'
|
|
1336
|
-
} else {
|
|
1337
|
-
headers = headers || {}
|
|
1338
|
-
headers['Content-Type'] = 'application/octet-stream'
|
|
1339
|
-
}
|
|
1340
|
-
} else {
|
|
1341
|
-
this.log('No agent profile was included.')
|
|
1342
|
-
return false
|
|
1343
|
-
}
|
|
1344
|
-
|
|
1345
|
-
ADL.XHR_request(
|
|
1346
|
-
this.lrs,
|
|
1347
|
-
url,
|
|
1348
|
-
method,
|
|
1349
|
-
profileval,
|
|
1350
|
-
this.lrs.auth,
|
|
1351
|
-
callback,
|
|
1352
|
-
null,
|
|
1353
|
-
false,
|
|
1354
|
-
headers,
|
|
1355
|
-
this.withCredentials,
|
|
1356
|
-
this.strictCallbacks
|
|
1357
|
-
)
|
|
1358
|
-
}
|
|
1359
|
-
}
|
|
1360
|
-
|
|
1361
|
-
/*
|
|
1362
|
-
* Get agnet profile from the LRS
|
|
1363
|
-
* @param {object} agent the agent associated with this profile
|
|
1364
|
-
* @param {string} [profileid] the id of the profile, if not included, the response will be a list of profileids
|
|
1365
|
-
* associated with the agent
|
|
1366
|
-
* @param {object} [since] date object or date string telling the LRS to return objects newer than the date supplied
|
|
1367
|
-
* @param {function} [callback] function to be called after the LRS responds
|
|
1368
|
-
* to this request (makes the call asynchronous)
|
|
1369
|
-
* the function will be passed the XMLHttpRequest object
|
|
1370
|
-
* @return {object} xhr response object or null if 404
|
|
1371
|
-
* @example
|
|
1372
|
-
* ADL.XAPIWrapper.getAgentProfile({"mbox":"mailto:tom@example.com"},
|
|
1373
|
-
* "agentprofile", null,
|
|
1374
|
-
* function(r){ADL.XAPIWrapper.log(JSON.parse(r.response));});
|
|
1375
|
-
* >> {info: "the agent profile"}
|
|
1376
|
-
*/
|
|
1377
|
-
XAPIWrapper.prototype.getAgentProfile = function (
|
|
1378
|
-
agent,
|
|
1379
|
-
profileid,
|
|
1380
|
-
since,
|
|
1381
|
-
callback
|
|
1382
|
-
) {
|
|
1383
|
-
if (this.testConfig()) {
|
|
1384
|
-
let url = this.lrs.endpoint + 'agents/profile?agent=<agent>'
|
|
1385
|
-
|
|
1386
|
-
url = url.replace('<agent>', encodeURIComponent(JSON.stringify(agent)))
|
|
1387
|
-
url = url.replace('<profileid>', encodeURIComponent(profileid))
|
|
1388
|
-
|
|
1389
|
-
if (profileid) {
|
|
1390
|
-
url += '&profileId=' + encodeURIComponent(profileid)
|
|
1391
|
-
}
|
|
1392
|
-
|
|
1393
|
-
if (since) {
|
|
1394
|
-
since = isDate(since)
|
|
1395
|
-
if (since != null) {
|
|
1396
|
-
url += '&since=' + encodeURIComponent(since.toISOString())
|
|
1397
|
-
}
|
|
1398
|
-
}
|
|
1399
|
-
|
|
1400
|
-
let result = ADL.XHR_request(
|
|
1401
|
-
this.lrs,
|
|
1402
|
-
url,
|
|
1403
|
-
'GET',
|
|
1404
|
-
null,
|
|
1405
|
-
this.lrs.auth,
|
|
1406
|
-
callback,
|
|
1407
|
-
null,
|
|
1408
|
-
true,
|
|
1409
|
-
null,
|
|
1410
|
-
this.withCredentials,
|
|
1411
|
-
this.strictCallbacks
|
|
1412
|
-
)
|
|
1413
|
-
|
|
1414
|
-
if (result === undefined || result.status == 404) {
|
|
1415
|
-
return null
|
|
1416
|
-
}
|
|
1417
|
-
|
|
1418
|
-
try {
|
|
1419
|
-
return JSON.parse(result.response)
|
|
1420
|
-
} catch (e) {
|
|
1421
|
-
return result.response
|
|
1422
|
-
}
|
|
1423
|
-
}
|
|
1424
|
-
}
|
|
1425
|
-
|
|
1426
|
-
/*
|
|
1427
|
-
* Delete agent profile in the LRS
|
|
1428
|
-
* @param {oject} agent the id of the Agent this profile is about
|
|
1429
|
-
* @param {string} profileid the id you want associated with this profile
|
|
1430
|
-
* @param {string} [matchHash] the hash of the profile to replace or * to replace any
|
|
1431
|
-
* @param {string} [noneMatchHash] the hash of the current profile or * to indicate no previous profile
|
|
1432
|
-
* @param {string} [callback] function to be called after the LRS responds
|
|
1433
|
-
* to this request (makes the call asynchronous)
|
|
1434
|
-
* the function will be passed the XMLHttpRequest object
|
|
1435
|
-
* @return {object} xhr response object or null if 404
|
|
1436
|
-
* @example
|
|
1437
|
-
* ADL.XAPIWrapper.deleteAgentProfile({"mbox":"mailto:tom@example.com"},
|
|
1438
|
-
* "agentprofile");
|
|
1439
|
-
* >> XMLHttpRequest {statusText: "NO CONTENT", status: 204, response: "", responseType: "", responseXML: null…}
|
|
1440
|
-
*/
|
|
1441
|
-
XAPIWrapper.prototype.deleteAgentProfile = function (
|
|
1442
|
-
agent,
|
|
1443
|
-
profileid,
|
|
1444
|
-
matchHash,
|
|
1445
|
-
noneMatchHash,
|
|
1446
|
-
callback
|
|
1447
|
-
) {
|
|
1448
|
-
if (this.testConfig()) {
|
|
1449
|
-
let url =
|
|
1450
|
-
this.lrs.endpoint + 'agents/profile?agent=<agent>&profileId=<profileid>'
|
|
1451
|
-
|
|
1452
|
-
url = url.replace('<agent>', encodeURIComponent(JSON.stringify(agent)))
|
|
1453
|
-
url = url.replace('<profileid>', encodeURIComponent(profileid))
|
|
1454
|
-
|
|
1455
|
-
let headers = null
|
|
1456
|
-
if (matchHash && noneMatchHash) {
|
|
1457
|
-
log("Can't have both If-Match and If-None-Match")
|
|
1458
|
-
} else if (matchHash) {
|
|
1459
|
-
headers = { 'If-Match': ADL.formatHash(matchHash) }
|
|
1460
|
-
} else if (noneMatchHash) {
|
|
1461
|
-
headers = { 'If-None-Match': ADL.formatHash(noneMatchHash) }
|
|
1462
|
-
}
|
|
1463
|
-
|
|
1464
|
-
let result = ADL.XHR_request(
|
|
1465
|
-
this.lrs,
|
|
1466
|
-
url,
|
|
1467
|
-
'DELETE',
|
|
1468
|
-
null,
|
|
1469
|
-
this.lrs.auth,
|
|
1470
|
-
callback,
|
|
1471
|
-
null,
|
|
1472
|
-
false,
|
|
1473
|
-
headers,
|
|
1474
|
-
this.withCredentials,
|
|
1475
|
-
this.strictCallbacks
|
|
1476
|
-
)
|
|
1477
|
-
|
|
1478
|
-
if (result === undefined || result.status == 404) {
|
|
1479
|
-
return null
|
|
1480
|
-
}
|
|
1481
|
-
|
|
1482
|
-
try {
|
|
1483
|
-
return JSON.parse(result.response)
|
|
1484
|
-
} catch (e) {
|
|
1485
|
-
return result
|
|
1486
|
-
}
|
|
1487
|
-
}
|
|
1488
|
-
}
|
|
1489
|
-
|
|
1490
|
-
/*
|
|
1491
|
-
* Tests the configuration of the lrs object
|
|
1492
|
-
*/
|
|
1493
|
-
function testConfig() {
|
|
1494
|
-
try {
|
|
1495
|
-
return this.lrs.endpoint != undefined && this.lrs.endpoint != ''
|
|
1496
|
-
} catch (e) {
|
|
1497
|
-
return false
|
|
1498
|
-
}
|
|
1499
|
-
}
|
|
1500
|
-
|
|
1501
|
-
// outputs the message to the console if available
|
|
1502
|
-
function log(message) {
|
|
1503
|
-
if (!log.debug) return false
|
|
1504
|
-
try {
|
|
1505
|
-
message
|
|
1506
|
-
return true
|
|
1507
|
-
} catch (e) {
|
|
1508
|
-
return false
|
|
1509
|
-
}
|
|
1510
|
-
}
|
|
1511
|
-
|
|
1512
|
-
// merges two object
|
|
1513
|
-
function mergeRecursive(obj1, obj2) {
|
|
1514
|
-
for (let p in obj2) {
|
|
1515
|
-
if (obj2.hasOwnProperty(p) == false) continue
|
|
1516
|
-
|
|
1517
|
-
let prop = obj2[p]
|
|
1518
|
-
log(p + ' : ' + prop)
|
|
1519
|
-
try {
|
|
1520
|
-
// Property in destination object set; update its value.
|
|
1521
|
-
if (obj2[p].constructor == Object) {
|
|
1522
|
-
obj1[p] = mergeRecursive(obj1[p], obj2[p])
|
|
1523
|
-
} else {
|
|
1524
|
-
if (obj1 == undefined) {
|
|
1525
|
-
obj1 = new Object()
|
|
1526
|
-
}
|
|
1527
|
-
obj1[p] = obj2[p]
|
|
1528
|
-
}
|
|
1529
|
-
} catch (e) {
|
|
1530
|
-
if (obj1 == undefined) {
|
|
1531
|
-
obj1 = new Object()
|
|
1532
|
-
}
|
|
1533
|
-
// Property in destination object not set; create it and set its value.
|
|
1534
|
-
obj1[p] = obj2[p]
|
|
1535
|
-
}
|
|
1536
|
-
}
|
|
1537
|
-
|
|
1538
|
-
return obj1
|
|
1539
|
-
}
|
|
1540
|
-
|
|
1541
|
-
// iniitializes an lrs object with settings from
|
|
1542
|
-
// a config file and from the url query string
|
|
1543
|
-
function getLRSObject(config) {
|
|
1544
|
-
let lrsProps = [
|
|
1545
|
-
'endpoint',
|
|
1546
|
-
'auth',
|
|
1547
|
-
'actor',
|
|
1548
|
-
'registration',
|
|
1549
|
-
'activity_id',
|
|
1550
|
-
'grouping',
|
|
1551
|
-
'activity_platform'
|
|
1552
|
-
]
|
|
1553
|
-
let lrs = new Object()
|
|
1554
|
-
let qsVars, prop
|
|
1555
|
-
|
|
1556
|
-
qsVars = parseQueryString()
|
|
1557
|
-
if (qsVars !== undefined && Object.keys(qsVars).length !== 0) {
|
|
1558
|
-
for (let i = 0; i < lrsProps.length; i++) {
|
|
1559
|
-
prop = lrsProps[i]
|
|
1560
|
-
if (qsVars[prop]) {
|
|
1561
|
-
lrs[prop] = qsVars[prop]
|
|
1562
|
-
delete qsVars[prop]
|
|
1563
|
-
}
|
|
1564
|
-
}
|
|
1565
|
-
// if (Object.keys(qsVars).length !== 0) {
|
|
1566
|
-
// lrs.extended = qsVars;
|
|
1567
|
-
// }
|
|
1568
|
-
|
|
1569
|
-
lrs = mergeRecursive(config, lrs)
|
|
1570
|
-
} else {
|
|
1571
|
-
lrs = config
|
|
1572
|
-
}
|
|
1573
|
-
|
|
1574
|
-
return lrs
|
|
1575
|
-
}
|
|
1576
|
-
|
|
1577
|
-
// parses the params in the url query string
|
|
1578
|
-
function parseQueryString() {
|
|
1579
|
-
let qs, pairs, pair, ii, parsed
|
|
1580
|
-
|
|
1581
|
-
qs = window.location.search.substring(1)
|
|
1582
|
-
|
|
1583
|
-
pairs = qs.split('&')
|
|
1584
|
-
parsed = {}
|
|
1585
|
-
for (ii = 0; ii < pairs.length; ii++) {
|
|
1586
|
-
pair = pairs[ii].split('=')
|
|
1587
|
-
if (pair.length === 2 && pair[0]) {
|
|
1588
|
-
parsed[pair[0]] = decodeURIComponent(pair[1])
|
|
1589
|
-
}
|
|
1590
|
-
}
|
|
1591
|
-
|
|
1592
|
-
return parsed
|
|
1593
|
-
}
|
|
1594
|
-
|
|
1595
|
-
function delay() {
|
|
1596
|
-
let xhr = new XMLHttpRequest()
|
|
1597
|
-
let url = window.location + '?forcenocache=' + ADL.ruuid()
|
|
1598
|
-
xhr.open('GET', url, false)
|
|
1599
|
-
xhr.send(null)
|
|
1600
|
-
}
|
|
1601
|
-
|
|
1602
|
-
/*
|
|
1603
|
-
* formats a request in a way that IE will allow
|
|
1604
|
-
* @param {string} method the http request method (ex: "PUT", "GET")
|
|
1605
|
-
* @param {string} url the url to the request (ex: ADL.XAPIWrapper.lrs.endpoint + "statements")
|
|
1606
|
-
* @param {array} [headers] headers to include in the request
|
|
1607
|
-
* @param {string} [data] the body of the request, if there is one
|
|
1608
|
-
* @return {object} xhr response object
|
|
1609
|
-
*/
|
|
1610
|
-
// function ie_request(method, url, headers, data) {
|
|
1611
|
-
// let newUrl = url
|
|
1612
|
-
|
|
1613
|
-
// //Everything that was on query string goes into form vars
|
|
1614
|
-
// let formData = new Array()
|
|
1615
|
-
// let qsIndex = newUrl.indexOf('?')
|
|
1616
|
-
// if (qsIndex > 0) {
|
|
1617
|
-
// formData.push(newUrl.substring(qsIndex + 1))
|
|
1618
|
-
// newUrl = newUrl.substring(0, qsIndex)
|
|
1619
|
-
// }
|
|
1620
|
-
|
|
1621
|
-
// //Method has to go on querystring, and nothing else
|
|
1622
|
-
// newUrl = newUrl + '?method=' + method
|
|
1623
|
-
|
|
1624
|
-
// //Headers
|
|
1625
|
-
// if (headers !== null) {
|
|
1626
|
-
// for (let headerName in headers) {
|
|
1627
|
-
// if (headers.hasOwnProperty(headerName))
|
|
1628
|
-
// formData.push(
|
|
1629
|
-
// headerName + '=' + encodeURIComponent(headers[headerName])
|
|
1630
|
-
// )
|
|
1631
|
-
// }
|
|
1632
|
-
// }
|
|
1633
|
-
|
|
1634
|
-
// //The original data is repackaged as "content" form var
|
|
1635
|
-
// if (data !== null) {
|
|
1636
|
-
// formData.push('content=' + encodeURIComponent(data))
|
|
1637
|
-
// }
|
|
1638
|
-
|
|
1639
|
-
// return {
|
|
1640
|
-
// method: 'POST',
|
|
1641
|
-
// url: newUrl,
|
|
1642
|
-
// headers: {},
|
|
1643
|
-
// data: formData.join('&')
|
|
1644
|
-
// }
|
|
1645
|
-
// }
|
|
1646
|
-
|
|
1647
|
-
/*!
|
|
1648
|
-
Excerpt from: Math.uuid.js (v1.4)
|
|
1649
|
-
http://www.broofa.com
|
|
1650
|
-
mailto:robert@broofa.com
|
|
1651
|
-
Copyright (c) 2010 Robert Kieffer
|
|
1652
|
-
Dual licensed under the MIT and GPL licenses.
|
|
1653
|
-
*/
|
|
1654
|
-
ADL.ruuid = function () {
|
|
1655
|
-
return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
|
|
1656
|
-
/[xy]/g,
|
|
1657
|
-
function (c) {
|
|
1658
|
-
let r = (Math.random() * 16) | 0,
|
|
1659
|
-
v = c == 'x' ? r : (r & 0x3) | 0x8
|
|
1660
|
-
return v.toString(16)
|
|
1661
|
-
}
|
|
1662
|
-
)
|
|
1663
|
-
}
|
|
1664
|
-
|
|
1665
|
-
/*
|
|
1666
|
-
* dateFromISOString
|
|
1667
|
-
* parses an ISO string into a date object
|
|
1668
|
-
* isostr - the ISO string
|
|
1669
|
-
*/
|
|
1670
|
-
ADL.dateFromISOString = function (isostr) {
|
|
1671
|
-
let regexp =
|
|
1672
|
-
'([0-9]{4})(-([0-9]{2})(-([0-9]{2})' +
|
|
1673
|
-
'([T| ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(.([0-9]+))?)?' +
|
|
1674
|
-
'(Z|(([-+])([0-9]{2}):([0-9]{2})))?)?)?)?'
|
|
1675
|
-
let d = isostr.match(new RegExp(regexp))
|
|
1676
|
-
|
|
1677
|
-
let offset = 0
|
|
1678
|
-
let date = new Date(d[1], 0, 1)
|
|
1679
|
-
|
|
1680
|
-
if (d[3]) {
|
|
1681
|
-
date.setMonth(d[3] - 1)
|
|
1682
|
-
}
|
|
1683
|
-
if (d[5]) {
|
|
1684
|
-
date.setDate(d[5])
|
|
1685
|
-
}
|
|
1686
|
-
if (d[7]) {
|
|
1687
|
-
date.setHours(d[7])
|
|
1688
|
-
}
|
|
1689
|
-
if (d[8]) {
|
|
1690
|
-
date.setMinutes(d[8])
|
|
1691
|
-
}
|
|
1692
|
-
if (d[10]) {
|
|
1693
|
-
date.setSeconds(d[10])
|
|
1694
|
-
}
|
|
1695
|
-
if (d[12]) {
|
|
1696
|
-
date.setMilliseconds(Number('0.' + d[12]) * 1000)
|
|
1697
|
-
}
|
|
1698
|
-
if (d[14]) {
|
|
1699
|
-
offset = Number(d[16]) * 60 + Number(d[17])
|
|
1700
|
-
offset *= d[15] == '-' ? 1 : -1
|
|
1701
|
-
}
|
|
1702
|
-
|
|
1703
|
-
offset -= date.getTimezoneOffset()
|
|
1704
|
-
let time = Number(date) + offset * 60 * 1000
|
|
1705
|
-
|
|
1706
|
-
let dateToReturn = new Date()
|
|
1707
|
-
dateToReturn.setTime(Number(time))
|
|
1708
|
-
return dateToReturn
|
|
1709
|
-
}
|
|
1710
|
-
|
|
1711
|
-
// Synchronous if callback is not provided (not recommended)
|
|
1712
|
-
/*
|
|
1713
|
-
* makes a request to a server (if possible, use functions provided in XAPIWrapper)
|
|
1714
|
-
* @param {string} lrs the lrs connection info, such as endpoint, auth, etc
|
|
1715
|
-
* @param {string} url the url of this request
|
|
1716
|
-
* @param {string} method the http request method
|
|
1717
|
-
* @param {string} data the payload
|
|
1718
|
-
* @param {string} auth the value for the Authorization header
|
|
1719
|
-
* @param {function} callback function to be called after the LRS responds
|
|
1720
|
-
* to this request (makes the call asynchronous)
|
|
1721
|
-
* @param {object} [callbackargs] additional javascript object to be passed to the callback function
|
|
1722
|
-
* @param {boolean} ignore404 allow page not found errors to pass
|
|
1723
|
-
* @param {object} extraHeaders other header key-values to be added to this request
|
|
1724
|
-
* @param {boolean} withCredentials
|
|
1725
|
-
* @param {boolean} strictCallbacks Callback must be executed and first param is error or null if no error
|
|
1726
|
-
* @return {object} xhr response object
|
|
1727
|
-
*/
|
|
1728
|
-
ADL.XHR_request = function (
|
|
1729
|
-
lrs,
|
|
1730
|
-
url,
|
|
1731
|
-
method,
|
|
1732
|
-
data,
|
|
1733
|
-
auth,
|
|
1734
|
-
callback,
|
|
1735
|
-
callbackargs,
|
|
1736
|
-
ignore404,
|
|
1737
|
-
extraHeaders,
|
|
1738
|
-
withCredentials,
|
|
1739
|
-
strictCallbacks
|
|
1740
|
-
) {
|
|
1741
|
-
'use strict'
|
|
1742
|
-
|
|
1743
|
-
let xhr,
|
|
1744
|
-
finished = false,
|
|
1745
|
-
xDomainRequest = false,
|
|
1746
|
-
ieXDomain = false,
|
|
1747
|
-
ieModeRequest,
|
|
1748
|
-
urlparts = url.toLowerCase().match(/^(.+):\/\/([^:/]*):?(\d+)?(\/.*)?$/),
|
|
1749
|
-
location = window.location,
|
|
1750
|
-
urlPort,
|
|
1751
|
-
result,
|
|
1752
|
-
extended,
|
|
1753
|
-
prop,
|
|
1754
|
-
until
|
|
1755
|
-
|
|
1756
|
-
//Consolidate headers
|
|
1757
|
-
let headers = {}
|
|
1758
|
-
headers['Content-Type'] = 'application/json'
|
|
1759
|
-
headers['Authorization'] = auth
|
|
1760
|
-
headers['X-Experience-API-Version'] = ADL.XAPIWrapper.xapiVersion
|
|
1761
|
-
if (extraHeaders !== null) {
|
|
1762
|
-
for (let headerName in extraHeaders) {
|
|
1763
|
-
if (extraHeaders.hasOwnProperty(headerName))
|
|
1764
|
-
headers[headerName] = extraHeaders[headerName]
|
|
1765
|
-
}
|
|
1766
|
-
}
|
|
1767
|
-
|
|
1768
|
-
//See if this really is a cross domain
|
|
1769
|
-
xDomainRequest =
|
|
1770
|
-
location.protocol.toLowerCase() !== urlparts[1] ||
|
|
1771
|
-
location.hostname.toLowerCase() !== urlparts[2]
|
|
1772
|
-
if (!xDomainRequest) {
|
|
1773
|
-
urlPort =
|
|
1774
|
-
urlparts[3] === null
|
|
1775
|
-
? urlparts[1] === 'http'
|
|
1776
|
-
? '80'
|
|
1777
|
-
: '443'
|
|
1778
|
-
: urlparts[3]
|
|
1779
|
-
xDomainRequest = urlPort === location.port
|
|
1780
|
-
}
|
|
1781
|
-
|
|
1782
|
-
//Add extended LMS-specified values to the URL
|
|
1783
|
-
if (lrs !== null && lrs.extended !== undefined) {
|
|
1784
|
-
extended = new Array()
|
|
1785
|
-
for (prop in lrs.extended) {
|
|
1786
|
-
extended.push(prop + '=' + encodeURIComponent(lrs.extended[prop]))
|
|
1787
|
-
}
|
|
1788
|
-
if (extended.length > 0) {
|
|
1789
|
-
url += (url.indexOf('?') > -1 ? '&' : '?') + extended.join('&')
|
|
1790
|
-
}
|
|
1791
|
-
}
|
|
1792
|
-
|
|
1793
|
-
//If it's not cross domain or we're not using IE, use the usual XmlHttpRequest
|
|
1794
|
-
let windowsVersionCheck =
|
|
1795
|
-
window.XDomainRequest &&
|
|
1796
|
-
window.XMLHttpRequest &&
|
|
1797
|
-
new XMLHttpRequest().responseType === undefined
|
|
1798
|
-
if (
|
|
1799
|
-
!xDomainRequest ||
|
|
1800
|
-
windowsVersionCheck === undefined ||
|
|
1801
|
-
windowsVersionCheck === false
|
|
1802
|
-
) {
|
|
1803
|
-
xhr = new XMLHttpRequest()
|
|
1804
|
-
xhr.withCredentials = withCredentials //allow cross domain cookie based auth
|
|
1805
|
-
xhr.open(method, url, callback != null)
|
|
1806
|
-
for (let headerName in headers) {
|
|
1807
|
-
xhr.setRequestHeader(headerName, headers[headerName])
|
|
1808
|
-
}
|
|
1809
|
-
}
|
|
1810
|
-
//Otherwise, use IE's XDomainRequest object
|
|
1811
|
-
else {
|
|
1812
|
-
ieXDomain = true
|
|
1813
|
-
// ieModeRequest = ie_request(method, url, headers, data)
|
|
1814
|
-
// xhr = new XDomainRequest()
|
|
1815
|
-
xhr.open(ieModeRequest.method, ieModeRequest.url)
|
|
1816
|
-
}
|
|
1817
|
-
|
|
1818
|
-
//Setup request callback
|
|
1819
|
-
function requestComplete() {
|
|
1820
|
-
if (!finished) {
|
|
1821
|
-
// may be in sync or async mode, using XMLHttpRequest or IE XDomainRequest, onreadystatechange or
|
|
1822
|
-
// onload or both might fire depending upon browser, just covering all bases with event hooks and
|
|
1823
|
-
// using 'finished' flag to avoid triggering events multiple times
|
|
1824
|
-
finished = true
|
|
1825
|
-
let notFoundOk = ignore404 && xhr.status === 404
|
|
1826
|
-
if (
|
|
1827
|
-
xhr.status === undefined ||
|
|
1828
|
-
(xhr.status >= 200 && xhr.status < 400) ||
|
|
1829
|
-
notFoundOk
|
|
1830
|
-
) {
|
|
1831
|
-
if (callback) {
|
|
1832
|
-
if (callbackargs) {
|
|
1833
|
-
strictCallbacks
|
|
1834
|
-
? callback(null, xhr, callbackargs)
|
|
1835
|
-
: callback(xhr, callbackargs)
|
|
1836
|
-
} else {
|
|
1837
|
-
let body
|
|
1838
|
-
|
|
1839
|
-
try {
|
|
1840
|
-
body = JSON.parse(xhr.responseText)
|
|
1841
|
-
} catch (e) {
|
|
1842
|
-
body = xhr.responseText
|
|
1843
|
-
}
|
|
1844
|
-
|
|
1845
|
-
strictCallbacks ? callback(null, xhr, body) : callback(xhr, body)
|
|
1846
|
-
}
|
|
1847
|
-
} else {
|
|
1848
|
-
result = xhr
|
|
1849
|
-
return xhr
|
|
1850
|
-
}
|
|
1851
|
-
} else {
|
|
1852
|
-
let warning
|
|
1853
|
-
try {
|
|
1854
|
-
warning =
|
|
1855
|
-
'There was a problem communicating with the Learning Record Store. ( ' +
|
|
1856
|
-
xhr.status +
|
|
1857
|
-
' | ' +
|
|
1858
|
-
xhr.response +
|
|
1859
|
-
' )' +
|
|
1860
|
-
url
|
|
1861
|
-
} catch (ex) {
|
|
1862
|
-
warning = ex.toString()
|
|
1863
|
-
}
|
|
1864
|
-
ADL.XAPIWrapper.log(warning)
|
|
1865
|
-
ADL.xhrRequestOnError(
|
|
1866
|
-
xhr,
|
|
1867
|
-
method,
|
|
1868
|
-
url,
|
|
1869
|
-
callback,
|
|
1870
|
-
callbackargs,
|
|
1871
|
-
strictCallbacks
|
|
1872
|
-
)
|
|
1873
|
-
result = xhr
|
|
1874
|
-
return xhr
|
|
1875
|
-
}
|
|
1876
|
-
} else {
|
|
1877
|
-
return result
|
|
1878
|
-
}
|
|
1879
|
-
}
|
|
1880
|
-
|
|
1881
|
-
xhr.onreadystatechange = function () {
|
|
1882
|
-
if (xhr.readyState === 4) {
|
|
1883
|
-
return requestComplete()
|
|
1884
|
-
}
|
|
1885
|
-
}
|
|
1886
|
-
|
|
1887
|
-
xhr.onload = requestComplete
|
|
1888
|
-
xhr.onerror = requestComplete
|
|
1889
|
-
//xhr.onerror = ADL.xhrRequestOnError(xhr, method, url);
|
|
1890
|
-
|
|
1891
|
-
xhr.send(ieXDomain ? ieModeRequest.data : data)
|
|
1892
|
-
|
|
1893
|
-
if (!callback) {
|
|
1894
|
-
// synchronous
|
|
1895
|
-
if (ieXDomain) {
|
|
1896
|
-
// synchronous call in IE, with no asynchronous mode available.
|
|
1897
|
-
until = 1000 + new Date()
|
|
1898
|
-
while (new Date() < until && xhr.readyState !== 4 && !finished) {
|
|
1899
|
-
delay()
|
|
1900
|
-
}
|
|
1901
|
-
}
|
|
1902
|
-
return requestComplete()
|
|
1903
|
-
}
|
|
1904
|
-
}
|
|
1905
|
-
|
|
1906
|
-
/*
|
|
1907
|
-
* Holder for custom global error callback
|
|
1908
|
-
* @param {object} xhr xhr object or null
|
|
1909
|
-
* @param {string} method XMLHttpRequest request method
|
|
1910
|
-
* @param {string} url full endpoint url
|
|
1911
|
-
* @param {function} callback function to be called after the LRS responds
|
|
1912
|
-
* to this request (makes the call asynchronous)
|
|
1913
|
-
* @param {object} [callbackargs] additional javascript object to be passed to the callback function
|
|
1914
|
-
* @param {boolean} strictCallbacks Callback must be executed and first param is error or null if no error
|
|
1915
|
-
* @example
|
|
1916
|
-
* ADL.xhrRequestOnError = function(xhr, method, url, callback, callbackargs) {
|
|
1917
|
-
* console.log(xhr);
|
|
1918
|
-
* alert(xhr.status + " " + xhr.statusText + ": " + xhr.response);
|
|
1919
|
-
* };
|
|
1920
|
-
*/
|
|
1921
|
-
ADL.xhrRequestOnError = function (
|
|
1922
|
-
xhr,
|
|
1923
|
-
method,
|
|
1924
|
-
url,
|
|
1925
|
-
callback,
|
|
1926
|
-
callbackargs,
|
|
1927
|
-
strictCallbacks
|
|
1928
|
-
) {
|
|
1929
|
-
if (callback && strictCallbacks) {
|
|
1930
|
-
let status = xhr ? xhr.status : undefined
|
|
1931
|
-
let error
|
|
1932
|
-
if (status) {
|
|
1933
|
-
error = new Error('Request error: ' + xhr.status)
|
|
1934
|
-
} else if (status === 0 || status === null) {
|
|
1935
|
-
// 0 and null = aborted
|
|
1936
|
-
error = new Error('Request error: aborted')
|
|
1937
|
-
} else {
|
|
1938
|
-
error = new Error('Reqeust error: unknown')
|
|
1939
|
-
}
|
|
1940
|
-
|
|
1941
|
-
if (callbackargs) {
|
|
1942
|
-
callback(error, xhr, callbackargs)
|
|
1943
|
-
} else {
|
|
1944
|
-
let body
|
|
1945
|
-
|
|
1946
|
-
try {
|
|
1947
|
-
body = JSON.parse(xhr.responseText)
|
|
1948
|
-
} catch (e) {
|
|
1949
|
-
body = xhr.responseText
|
|
1950
|
-
}
|
|
1951
|
-
|
|
1952
|
-
callback(error, xhr, body)
|
|
1953
|
-
}
|
|
1954
|
-
}
|
|
1955
|
-
}
|
|
1956
|
-
|
|
1957
|
-
ADL.formatHash = function (hash) {
|
|
1958
|
-
return hash === '*' ? hash : '"' + hash + '"'
|
|
1959
|
-
}
|
|
1960
|
-
|
|
1961
|
-
ADL.XAPIWrapper = new XAPIWrapper(Config, false)
|
|
1962
|
-
//============================= END ==========================================
|
|
1963
|
-
}
|