fcad-core-dragon 2.1.0-beta.2 → 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.
Files changed (67) hide show
  1. package/.editorconfig +33 -33
  2. package/.eslintignore +29 -29
  3. package/.eslintrc.cjs +81 -81
  4. package/CHANGELOG +20 -0
  5. package/bk.scss +117 -117
  6. package/package.json +30 -31
  7. package/src/assets/data/onboardingMessages.json +47 -47
  8. package/src/components/AppBase.vue +167 -39
  9. package/src/components/AppBaseButton.test.js +0 -1
  10. package/src/components/AppBaseErrorDisplay.vue +438 -438
  11. package/src/components/AppBaseFlipCard.vue +84 -84
  12. package/src/components/AppBaseModule.vue +103 -116
  13. package/src/components/AppBasePage.vue +13 -13
  14. package/src/components/AppBasePopover.vue +41 -41
  15. package/src/components/AppCompMenu.vue +2 -1
  16. package/src/components/AppCompPlayBarNext.vue +157 -16
  17. package/src/components/AppCompPopUpNext.vue +3 -3
  18. package/src/components/AppCompQuizRecall.vue +2 -3
  19. package/src/components/AppCompSettingsMenu.vue +172 -172
  20. package/src/components/AppCompTableOfContent.vue +1 -1
  21. package/src/components/AppCompViewDisplay.vue +6 -6
  22. package/src/components/tests__/useTimer.spec.js +91 -0
  23. package/src/composables/useIdleDetector.js +56 -0
  24. package/src/composables/useQuiz.js +1 -1
  25. package/src/composables/useTimer.js +175 -0
  26. package/src/externalComps/ModuleView.vue +22 -22
  27. package/src/externalComps/SummaryView.vue +91 -91
  28. package/src/main.js +2 -0
  29. package/src/module/stores/appStore.js +10 -34
  30. package/src/module/xapi/ADL.js +1 -0
  31. package/src/module/xapi/Crypto/Hasher.js +241 -241
  32. package/src/module/xapi/Crypto/WordArray.js +278 -278
  33. package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +103 -103
  34. package/src/module/xapi/Crypto/algorithms/C_algo.js +315 -315
  35. package/src/module/xapi/Crypto/algorithms/HMAC.js +9 -9
  36. package/src/module/xapi/Crypto/algorithms/SHA1.js +9 -9
  37. package/src/module/xapi/Crypto/encoders/Base.js +105 -105
  38. package/src/module/xapi/Crypto/encoders/Base64.js +99 -99
  39. package/src/module/xapi/Crypto/encoders/Hex.js +61 -61
  40. package/src/module/xapi/Crypto/encoders/Latin1.js +61 -61
  41. package/src/module/xapi/Crypto/encoders/Utf8.js +45 -45
  42. package/src/module/xapi/Crypto/index.js +53 -53
  43. package/src/module/xapi/Statement/activity.js +47 -47
  44. package/src/module/xapi/Statement/agent.js +55 -55
  45. package/src/module/xapi/Statement/group.js +26 -26
  46. package/src/module/xapi/Statement/index.js +259 -259
  47. package/src/module/xapi/Statement/statement.js +253 -253
  48. package/src/module/xapi/Statement/statementRef.js +23 -23
  49. package/src/module/xapi/Statement/substatement.js +22 -22
  50. package/src/module/xapi/Statement/verb.js +36 -36
  51. package/src/module/xapi/activitytypes.js +17 -17
  52. package/src/module/xapi/utils.js +167 -167
  53. package/src/module/xapi/verbs.js +294 -294
  54. package/src/module/xapi/xapiStatement.js +444 -444
  55. package/src/plugins/analytics.js +34 -0
  56. package/src/plugins/bus.js +8 -8
  57. package/src/plugins/gsap.js +14 -14
  58. package/src/plugins/i18n.js +26 -44
  59. package/src/plugins/save.js +37 -37
  60. package/src/plugins/scorm.js +287 -287
  61. package/src/plugins/xapi.js +11 -11
  62. package/src/public/index.html +33 -33
  63. package/src/router/index.js +6 -3
  64. package/src/components/AppCompPlayBarProgress.vue +0 -82
  65. package/src/mixins/$mediaMixins.js +0 -809
  66. package/src/mixins/timerMixin.js +0 -195
  67. 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 += '&registration=' + 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 += '&registration=' + 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 += '&registration=' + 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
- }