fcad-core-dragon 2.0.0-beta.1 → 2.0.0-beta.10

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 (163) hide show
  1. package/.editorconfig +6 -31
  2. package/.prettierrc +11 -0
  3. package/.vscode/extensions.json +8 -0
  4. package/.vscode/settings.json +16 -0
  5. package/CHANGELOG +153 -0
  6. package/README.md +28 -43
  7. package/documentation/.vitepress/config.js +114 -0
  8. package/documentation/api-examples.md +49 -0
  9. package/documentation/composants/app-base-button.md +58 -0
  10. package/documentation/composants/app-base-error-display.md +59 -0
  11. package/documentation/composants/app-base-popover.md +68 -0
  12. package/documentation/composants/app-comp-audio.md +75 -0
  13. package/documentation/composants/app-comp-branch-buttons.md +111 -0
  14. package/documentation/composants/app-comp-button-progress.md +53 -0
  15. package/documentation/composants/app-comp-carousel.md +53 -0
  16. package/documentation/composants/app-comp-container.md +53 -0
  17. package/documentation/composants/app-comp-input-checkbox-next.md +42 -0
  18. package/documentation/composants/app-comp-input-dropdown-next.md +34 -0
  19. package/documentation/composants/app-comp-input-radio-next.md +39 -0
  20. package/documentation/composants/app-comp-input-text-next.md +35 -0
  21. package/documentation/composants/app-comp-input-text-table-next.md +34 -0
  22. package/documentation/composants/app-comp-input-text-to-fill-dropdown-next.md +53 -0
  23. package/documentation/composants/app-comp-input-text-to-fill-next.md +31 -0
  24. package/documentation/composants/app-comp-jauge.md +31 -0
  25. package/documentation/composants/app-comp-menu-item.md +55 -0
  26. package/documentation/composants/app-comp-menu.md +29 -0
  27. package/documentation/composants/app-comp-navigation.md +41 -0
  28. package/documentation/composants/app-comp-note-call.md +53 -0
  29. package/documentation/composants/app-comp-note-credit.md +53 -0
  30. package/documentation/composants/app-comp-play-bar-next.md +53 -0
  31. package/documentation/composants/app-comp-pop-up-next.md +93 -0
  32. package/documentation/composants/app-comp-quiz-next.md +235 -0
  33. package/documentation/composants/app-comp-quiz-recall.md +53 -0
  34. package/documentation/composants/app-comp-svg-next.md +53 -0
  35. package/documentation/composants/app-comp-table-of-content.md +50 -0
  36. package/documentation/composants/app-comp-video-player.md +82 -0
  37. package/documentation/composants.md +46 -0
  38. package/documentation/composants_critiques/ModelPageComposant.md +53 -0
  39. package/documentation/composants_critiques/app-base-module.md +43 -0
  40. package/documentation/composants_critiques/app-base-page.md +48 -0
  41. package/documentation/composants_critiques/app-base.md +311 -0
  42. package/documentation/composants_critiques/main.md +15 -0
  43. package/documentation/demarrage.md +50 -0
  44. package/documentation/deploiement.md +58 -0
  45. package/documentation/index.md +33 -0
  46. package/documentation/markdown-examples.md +85 -0
  47. package/documentation/public/npm_version.png +0 -0
  48. package/documentation/public/vite.svg +15 -0
  49. package/documentation/public/vuejs.svg +2 -0
  50. package/documentation/public/vuetify.svg +6 -0
  51. package/eslint.config.js +60 -0
  52. package/package.json +43 -47
  53. package/src/$locales/en.json +86 -108
  54. package/src/$locales/fr.json +66 -127
  55. package/src/assets/data/onboardingMessages.json +1 -1
  56. package/src/components/AppBase.vue +960 -405
  57. package/src/components/AppBaseButton.test.js +21 -0
  58. package/src/components/AppBaseButton.vue +42 -10
  59. package/src/components/AppBaseErrorDisplay.vue +207 -189
  60. package/src/components/AppBaseFlipCard.vue +1 -0
  61. package/src/components/AppBaseModule.vue +769 -977
  62. package/src/components/AppBasePage.vue +635 -81
  63. package/src/components/AppBasePopover.vue +41 -0
  64. package/src/components/AppBaseSkeleton.vue +66 -0
  65. package/src/components/AppCompAudio.vue +256 -0
  66. package/src/components/AppCompBranchButtons.vue +79 -153
  67. package/src/components/AppCompButtonProgress.vue +21 -36
  68. package/src/components/AppCompCarousel.vue +231 -87
  69. package/src/components/{AppCompTranscript.vue → AppCompContainer.vue} +12 -2
  70. package/src/components/AppCompInputCheckBoxNx.vue +323 -0
  71. package/src/components/AppCompInputDropdownNx.vue +299 -0
  72. package/src/components/AppCompInputRadioNx.vue +284 -0
  73. package/src/components/AppCompInputTextNx.vue +153 -0
  74. package/src/components/AppCompInputTextTableNx.vue +202 -0
  75. package/src/components/AppCompInputTextToFillDropdownNx.vue +340 -0
  76. package/src/components/AppCompInputTextToFillNx.vue +313 -0
  77. package/src/components/AppCompJauge.vue +36 -10
  78. package/src/components/AppCompMenu.vue +246 -32
  79. package/src/components/AppCompMenuItem.vue +87 -21
  80. package/src/components/AppCompNavigation.vue +470 -447
  81. package/src/components/AppCompNoteCall.vue +93 -58
  82. package/src/components/AppCompNoteCredit.vue +423 -96
  83. package/src/components/AppCompPlayBarNext.vue +2288 -0
  84. package/src/components/AppCompPopUpNext.vue +504 -0
  85. package/src/components/AppCompQuizNext.vue +510 -0
  86. package/src/components/AppCompQuizRecall.vue +199 -99
  87. package/src/components/AppCompSVGNext.vue +346 -0
  88. package/src/components/AppCompSettingsMenu.vue +17 -16
  89. package/src/components/AppCompTableOfContent.vue +262 -99
  90. package/src/components/AppCompVideoPlayer.vue +183 -142
  91. package/src/components/BaseModule.vue +8 -20
  92. package/src/components/tests__/AppBaseButton.spec.js +53 -0
  93. package/src/components/tests__/useTimer.spec.js +91 -0
  94. package/src/composables/useIdleDetector.js +56 -0
  95. package/src/composables/useQuiz.js +89 -0
  96. package/src/composables/useTimer.js +172 -0
  97. package/src/directives/nvdaFix.js +53 -0
  98. package/src/externalComps/ModuleView.vue +22 -0
  99. package/src/externalComps/SummaryView.vue +91 -0
  100. package/src/main.js +397 -148
  101. package/src/module/stores/appStore.js +947 -0
  102. package/src/module/xapi/ADL.js +241 -60
  103. package/src/module/xapi/Crypto/Hasher.js +8 -8
  104. package/src/module/xapi/Crypto/WordArray.js +6 -6
  105. package/src/module/xapi/Crypto/algorithms/BufferedBlockAlgorithm.js +4 -4
  106. package/src/module/xapi/Crypto/algorithms/C_algo.js +14 -18
  107. package/src/module/xapi/Crypto/algorithms/HMAC.js +1 -1
  108. package/src/module/xapi/Crypto/algorithms/SHA1.js +1 -1
  109. package/src/module/xapi/Crypto/encoders/Base.js +7 -7
  110. package/src/module/xapi/Crypto/encoders/Base64.js +3 -3
  111. package/src/module/xapi/Crypto/encoders/Hex.js +2 -2
  112. package/src/module/xapi/Crypto/encoders/Latin1.js +3 -3
  113. package/src/module/xapi/Crypto/encoders/Utf8.js +3 -3
  114. package/src/module/xapi/Statement/index.js +3 -3
  115. package/src/module/xapi/launch.js +10 -10
  116. package/src/module/xapi/utils.js +17 -17
  117. package/src/module/xapi/wrapper.js +219 -214
  118. package/src/module/xapi/xapiStatement.js +29 -29
  119. package/src/plugins/analytics.js +34 -0
  120. package/src/plugins/bus.js +7 -2
  121. package/src/plugins/gsap.js +5 -7
  122. package/src/plugins/helper.js +97 -34
  123. package/src/plugins/i18n.js +13 -18
  124. package/src/plugins/idb.js +45 -30
  125. package/src/plugins/save.js +1 -1
  126. package/src/plugins/scorm.js +15 -15
  127. package/src/plugins/xapi.js +2 -2
  128. package/src/public/index.html +22 -10
  129. package/src/router/index.js +29 -13
  130. package/src/router/routes.js +29 -54
  131. package/src/shared/generalfuncs.js +186 -30
  132. package/src/shared/validators.js +809 -40
  133. package/vitest.config.js +19 -0
  134. package/.eslintignore +0 -29
  135. package/.eslintrc.js +0 -86
  136. package/.prettierrc.js +0 -5
  137. package/babel.config.js +0 -3
  138. package/src/components/AppBaseDragChoice.vue +0 -91
  139. package/src/components/AppBaseDropZone.vue +0 -112
  140. package/src/components/AppCompBif.vue +0 -120
  141. package/src/components/AppCompDragAndDrop.vue +0 -339
  142. package/src/components/AppCompInputAssociation.vue +0 -332
  143. package/src/components/AppCompInputCheckBox.vue +0 -227
  144. package/src/components/AppCompInputDropdown.vue +0 -184
  145. package/src/components/AppCompInputRadio.vue +0 -169
  146. package/src/components/AppCompInputTextBox.vue +0 -91
  147. package/src/components/AppCompInputTextTable.vue +0 -155
  148. package/src/components/AppCompInputTextToFillDropdown.vue +0 -255
  149. package/src/components/AppCompInputTextToFillText.vue +0 -164
  150. package/src/components/AppCompMediaPlayer.vue +0 -397
  151. package/src/components/AppCompPlayBar.vue +0 -1319
  152. package/src/components/AppCompPopUp.vue +0 -522
  153. package/src/components/AppCompPopover.vue +0 -27
  154. package/src/components/AppCompQuiz.vue +0 -2989
  155. package/src/components/AppCompSVG.vue +0 -309
  156. package/src/mixins/$pageMixins.js +0 -459
  157. package/src/mixins/$quizMixins.js +0 -456
  158. package/src/mixins/timerMixin.js +0 -156
  159. package/src/module/store.js +0 -895
  160. package/src/plugins/timeManager.js +0 -77
  161. package/src/routes_bckp.js +0 -313
  162. package/src/routes_static.js +0 -344
  163. package/vue.config.js +0 -83
@@ -9,7 +9,7 @@ export function xapiwrapper(ADL) {
9
9
  // from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date/toISOString
10
10
 
11
11
  if (!Date.prototype.toISOString) {
12
- ;(function() {
12
+ ;(function () {
13
13
  function pad(number) {
14
14
  let r = String(number)
15
15
  if (r.length === 1) {
@@ -18,7 +18,7 @@ export function xapiwrapper(ADL) {
18
18
  return r
19
19
  }
20
20
 
21
- Date.prototype.toISOString = function() {
21
+ Date.prototype.toISOString = function () {
22
22
  return (
23
23
  this.getUTCFullYear() +
24
24
  '-' +
@@ -106,16 +106,16 @@ export function xapiwrapper(ADL) {
106
106
  c < 1 << 7
107
107
  ? 1
108
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
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
119
  }
120
120
  return byteLen
121
121
  }
@@ -131,7 +131,7 @@ export function xapiwrapper(ADL) {
131
131
  * };
132
132
  * ADL.XAPIWrapper.changeConfig(conf);
133
133
  */
134
- let Config = (function() {
134
+ let Config = (function () {
135
135
  let conf = {}
136
136
  conf['endpoint'] = 'http://localhost:8000/xapi/'
137
137
  //try
@@ -158,7 +158,7 @@ export function xapiwrapper(ADL) {
158
158
  * @param {object} config with a minimum of an endoint property
159
159
  * @param {boolean} verifyxapiversion indicating whether to verify the version of the LRS is compatible with this wrapper
160
160
  */
161
- let XAPIWrapper = function(config, verifyxapiversion) {
161
+ let XAPIWrapper = function (config, verifyxapiversion) {
162
162
  this.lrs = getLRSObject(config || {})
163
163
  if (this.lrs.user && this.lrs.password)
164
164
  updateAuth(this.lrs, this.lrs.user, this.lrs.password)
@@ -198,10 +198,10 @@ export function xapiwrapper(ADL) {
198
198
  'GET',
199
199
  null,
200
200
  null,
201
- function(r) {
201
+ function (r) {
202
202
  if (r.status == 200) {
203
203
  try {
204
- let lrsabout = JSON.parse(r.response)
204
+ let lrsabout = r.json()
205
205
  let versionOK = false
206
206
  for (let idx in lrsabout.version) {
207
207
  if (lrsabout.version.hasOwnProperty(idx))
@@ -237,12 +237,12 @@ export function xapiwrapper(ADL) {
237
237
  )
238
238
  }
239
239
 
240
- this.searchParams = function() {
240
+ this.searchParams = function () {
241
241
  let sp = { format: 'exact' }
242
242
  return sp
243
243
  }
244
244
 
245
- this.hash = function(tohash) {
245
+ this.hash = function (tohash) {
246
246
  if (!tohash) return null
247
247
  try {
248
248
  return toSHA1(tohash)
@@ -252,7 +252,7 @@ export function xapiwrapper(ADL) {
252
252
  }
253
253
  }
254
254
 
255
- this.changeConfig = function(config) {
255
+ this.changeConfig = function (config) {
256
256
  try {
257
257
  ADL.XAPIWrapper.log('updating lrs object with new configuration')
258
258
  this.lrs = mergeRecursive(this.lrs, config)
@@ -277,7 +277,7 @@ export function xapiwrapper(ADL) {
277
277
  * These values could be initialized from the Config object or from the url query string.
278
278
  * @param {object} stmt the statement object
279
279
  */
280
- XAPIWrapper.prototype.prepareStatement = function(stmt) {
280
+ XAPIWrapper.prototype.prepareStatement = function (stmt) {
281
281
  if (stmt.actor === undefined) {
282
282
  stmt.actor = JSON.parse(this.lrs.actor)
283
283
  } else if (typeof stmt.actor === 'string') {
@@ -354,7 +354,7 @@ export function xapiwrapper(ADL) {
354
354
  * ADL.XAPIWrapper.log("[" + obj.id + "]: " + resp.status + " - " + resp.statusText);});
355
355
  * >> [4edfe763-8b84-41f1-a355-78b7601a6fe8]: 204 - NO CONTENT
356
356
  */
357
- XAPIWrapper.prototype.sendStatement = function(stmt, callback, attachments) {
357
+ XAPIWrapper.prototype.sendStatement = function (stmt, callback, attachments) {
358
358
  if (this.testConfig()) {
359
359
  this.prepareStatement(stmt)
360
360
  let id
@@ -384,7 +384,7 @@ export function xapiwrapper(ADL) {
384
384
  this.withCredentials,
385
385
  this.strictCallbacks
386
386
  )
387
- if (!callback) return { xhr: resp, id: id }
387
+ if (!callback) return { xhr: resp[0], id: id }
388
388
  }
389
389
  }
390
390
 
@@ -414,7 +414,7 @@ export function xapiwrapper(ADL) {
414
414
  * ADL.XAPIWrapper.log("[" + obj.id + "]: " + resp.status + " - " + resp.statusText);});
415
415
  * >> [4edfe763-8b84-41f1-a355-78b7601a6fe8]: 204 - NO CONTENT
416
416
  */
417
- XAPIWrapper.prototype.sendStatementWithFetch = async function(
417
+ XAPIWrapper.prototype.sendStatementWithFetch = async function (
418
418
  stmt,
419
419
  callback,
420
420
  attachments
@@ -452,13 +452,13 @@ export function xapiwrapper(ADL) {
452
452
  }
453
453
  }
454
454
 
455
- XAPIWrapper.prototype.stringToArrayBuffer = function(content, encoding) {
455
+ XAPIWrapper.prototype.stringToArrayBuffer = function (content, encoding) {
456
456
  encoding = encoding || ADL.XAPIWrapper.defaultEncoding
457
457
 
458
458
  return new TextEncoder(encoding).encode(content).buffer
459
459
  }
460
460
 
461
- XAPIWrapper.prototype.stringFromArrayBuffer = function(content, encoding) {
461
+ XAPIWrapper.prototype.stringFromArrayBuffer = function (content, encoding) {
462
462
  encoding = encoding || ADL.XAPIWrapper.defaultEncoding
463
463
 
464
464
  return new TextDecoder(encoding).decode(content)
@@ -477,7 +477,7 @@ export function xapiwrapper(ADL) {
477
477
  value : a UTF8 string containing the binary data of the attachment. For string values, this can just be the JS string.
478
478
  }
479
479
  */
480
- XAPIWrapper.prototype.buildMultipartPost = function(
480
+ XAPIWrapper.prototype.buildMultipartPost = function (
481
481
  statement,
482
482
  attachments,
483
483
  extraHeaders
@@ -575,7 +575,7 @@ export function xapiwrapper(ADL) {
575
575
  * "context": {"registration": "51a6f860-1997-11e3-8ffd-0800200c9a66"},
576
576
  * "id": "ea9c1d01-0606-4ec7-8e5d-20f87b1211ed"}
577
577
  */
578
- XAPIWrapper.prototype.sendStatements = function(stmtArray, callback) {
578
+ XAPIWrapper.prototype.sendStatements = function (stmtArray, callback) {
579
579
  if (this.testConfig()) {
580
580
  for (let i in stmtArray) {
581
581
  if (stmtArray.hasOwnProperty(i)) this.prepareStatement(stmtArray[i])
@@ -600,9 +600,64 @@ export function xapiwrapper(ADL) {
600
600
  }
601
601
  }
602
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
+
636
+ let headers = {}
637
+ headers['Content-type'] = 'application/json; charset=UTF-8'
638
+ headers['Authorization'] = this.lrs.auth
639
+ headers['X-Experience-API-Version'] = ADL.XAPIWrapper.xapiVersion
640
+
641
+ let payload = JSON.stringify(stmtArray)
642
+
643
+ // fecth API used to allow the delivery of the request after the browser is closed
644
+ let resp = await fetch(this.lrs.endpoint + 'statements', {
645
+ method: 'POST',
646
+ headers,
647
+ body: payload,
648
+ keepalive: true // allow the request to outlive the closing of browser tab
649
+ })
650
+
651
+ //=============================================
652
+ if (!callback) {
653
+ return resp
654
+ }
655
+ }
656
+ }
657
+
603
658
  /*
604
659
  * Get statement(s) based on the searchparams or more url.
605
- * @param {object} searchparams an ADL.XAPIWrapper.searchParams object of
660
+ * @param {object || array } searchparams an ADL.XAPIWrapper.searchParams object of
606
661
  * key(search parameter)-value(parameter value) pairs.
607
662
  * Example:
608
663
  * let myparams = ADL.XAPIWrapper.searchParams();
@@ -622,30 +677,46 @@ export function xapiwrapper(ADL) {
622
677
  *
623
678
  * >> <Array of statements>
624
679
  */
625
- XAPIWrapper.prototype.getStatements = function(searchparams, more, callback) {
680
+ XAPIWrapper.prototype.getStatements = async function (
681
+ searchparams,
682
+ more,
683
+ callback
684
+ ) {
685
+ const listSearchParams = []
686
+
687
+ searchparams.constructor == Array
688
+ ? listSearchParams.push(...searchparams)
689
+ : listSearchParams.push(searchparams)
690
+
626
691
  if (this.testConfig()) {
627
692
  let url = this.lrs.endpoint + 'statements'
693
+ const URLS = []
628
694
  if (more) {
629
695
  url = this.base + more
630
696
  } else {
631
- let urlparams = new Array()
632
-
633
- for (let s in searchparams) {
634
- if (searchparams.hasOwnProperty(s)) {
635
- if (s == 'until' || s == 'since') {
636
- let d = new Date(searchparams[s])
637
- urlparams.push(s + '=' + encodeURIComponent(d.toISOString()))
638
- } else {
639
- urlparams.push(s + '=' + encodeURIComponent(searchparams[s]))
697
+ for (let searchparams of listSearchParams) {
698
+ let urlparams = new Array()
699
+ let _urls = url
700
+ for (let s in searchparams) {
701
+ if (searchparams.hasOwnProperty(s)) {
702
+ if (s == 'until' || s == 'since') {
703
+ let d = new Date(searchparams[s])
704
+ urlparams.push(s + '=' + encodeURIComponent(d.toISOString()))
705
+ } else {
706
+ urlparams.push(s + '=' + encodeURIComponent(searchparams[s]))
707
+ }
640
708
  }
641
709
  }
710
+ if (urlparams.length > 0) {
711
+ _urls = _urls + '?' + urlparams.join('&')
712
+ }
713
+ URLS.push(_urls)
642
714
  }
643
- if (urlparams.length > 0) url = url + '?' + urlparams.join('&')
644
715
  }
645
716
 
646
- let res = ADL.XHR_request(
717
+ let res = await ADL.XHR_request(
647
718
  this.lrs,
648
- url,
719
+ URLS,
649
720
  'GET',
650
721
  null,
651
722
  this.lrs.auth,
@@ -662,9 +733,9 @@ export function xapiwrapper(ADL) {
662
733
  }
663
734
 
664
735
  try {
665
- return JSON.parse(res.response)
736
+ return await res
666
737
  } catch (e) {
667
- return res.response
738
+ return e
668
739
  }
669
740
  }
670
741
  }
@@ -681,7 +752,7 @@ export function xapiwrapper(ADL) {
681
752
  * ADL.XAPIWrapper.log(res);
682
753
  * >> <Activity object>
683
754
  */
684
- XAPIWrapper.prototype.getActivities = function(activityid, callback) {
755
+ XAPIWrapper.prototype.getActivities = async function (activityid, callback) {
685
756
  if (this.testConfig()) {
686
757
  let url = this.lrs.endpoint + 'activities?activityId=<activityid>'
687
758
  url = url.replace('<activityid>', encodeURIComponent(activityid))
@@ -705,9 +776,9 @@ export function xapiwrapper(ADL) {
705
776
  }
706
777
 
707
778
  try {
708
- return JSON.parse(result.response)
779
+ return await result[0].json()
709
780
  } catch (e) {
710
- return result.response
781
+ return result
711
782
  }
712
783
  }
713
784
  }
@@ -731,7 +802,7 @@ export function xapiwrapper(ADL) {
731
802
  * {"mbox":"mailto:tom@example.com"},
732
803
  * "questionstate", null, stateval);
733
804
  */
734
- XAPIWrapper.prototype.sendState = function(
805
+ XAPIWrapper.prototype.sendState = function (
735
806
  activityid,
736
807
  agent,
737
808
  stateid,
@@ -817,7 +888,7 @@ export function xapiwrapper(ADL) {
817
888
  * {"mbox":"mailto:tom@example.com"}, "questionstate");
818
889
  * >> {info: "the state info"}
819
890
  */
820
- XAPIWrapper.prototype.getState = function(
891
+ XAPIWrapper.prototype.getState = async function (
821
892
  activityid,
822
893
  agent,
823
894
  stateid,
@@ -867,9 +938,9 @@ export function xapiwrapper(ADL) {
867
938
  }
868
939
 
869
940
  try {
870
- return JSON.parse(result.response)
941
+ return await result[0].json()
871
942
  } catch (e) {
872
- return result.response
943
+ return e
873
944
  }
874
945
  }
875
946
  }
@@ -903,7 +974,7 @@ export function xapiwrapper(ADL) {
903
974
  * {"mbox":"mailto:tom@example.com"}, "questionstate");
904
975
  * >> 404
905
976
  */
906
- XAPIWrapper.prototype.deleteState = function(
977
+ XAPIWrapper.prototype.deleteState = async function (
907
978
  activityid,
908
979
  agent,
909
980
  stateid,
@@ -953,7 +1024,7 @@ export function xapiwrapper(ADL) {
953
1024
  }
954
1025
 
955
1026
  try {
956
- return JSON.parse(result.response)
1027
+ return await result[0].json()
957
1028
  } catch (e) {
958
1029
  return result
959
1030
  }
@@ -976,7 +1047,7 @@ export function xapiwrapper(ADL) {
976
1047
  * ADL.XAPIWrapper.sendActivityProfile("http://adlnet.gov/expapi/activities/question",
977
1048
  * "actprofile", profile, null, "*");
978
1049
  */
979
- XAPIWrapper.prototype.sendActivityProfile = function(
1050
+ XAPIWrapper.prototype.sendActivityProfile = function (
980
1051
  activityid,
981
1052
  profileid,
982
1053
  profileval,
@@ -1053,7 +1124,7 @@ export function xapiwrapper(ADL) {
1053
1124
  * function(r){ADL.XAPIWrapper.log(JSON.parse(r.response));});
1054
1125
  * >> {info: "the profile"}
1055
1126
  */
1056
- XAPIWrapper.prototype.getActivityProfile = function(
1127
+ XAPIWrapper.prototype.getActivityProfile = async function (
1057
1128
  activityid,
1058
1129
  profileid,
1059
1130
  since,
@@ -1095,9 +1166,9 @@ export function xapiwrapper(ADL) {
1095
1166
  }
1096
1167
 
1097
1168
  try {
1098
- return JSON.parse(result.response)
1169
+ return result[0].json()
1099
1170
  } catch (e) {
1100
- return result.response
1171
+ return result
1101
1172
  }
1102
1173
  }
1103
1174
  }
@@ -1117,7 +1188,7 @@ export function xapiwrapper(ADL) {
1117
1188
  * "actprofile");
1118
1189
  * >> XMLHttpRequest {statusText: "NO CONTENT", status: 204, response: "", responseType: "", responseXML: null…}
1119
1190
  */
1120
- XAPIWrapper.prototype.deleteActivityProfile = function(
1191
+ XAPIWrapper.prototype.deleteActivityProfile = async function (
1121
1192
  activityid,
1122
1193
  profileid,
1123
1194
  matchHash,
@@ -1160,7 +1231,7 @@ export function xapiwrapper(ADL) {
1160
1231
  }
1161
1232
 
1162
1233
  try {
1163
- return JSON.parse(result.response)
1234
+ return result[0].json()
1164
1235
  } catch (e) {
1165
1236
  return result
1166
1237
  }
@@ -1181,7 +1252,7 @@ export function xapiwrapper(ADL) {
1181
1252
  * ADL.XAPIWrapper.log(res);
1182
1253
  * >> <Person object>
1183
1254
  */
1184
- XAPIWrapper.prototype.getAgents = function(agent, callback) {
1255
+ XAPIWrapper.prototype.getAgents = async function (agent, callback) {
1185
1256
  if (this.testConfig()) {
1186
1257
  let url = this.lrs.endpoint + 'agents?agent=<agent>'
1187
1258
  url = url.replace('<agent>', encodeURIComponent(JSON.stringify(agent)))
@@ -1205,9 +1276,9 @@ export function xapiwrapper(ADL) {
1205
1276
  }
1206
1277
 
1207
1278
  try {
1208
- return JSON.parse(result.response)
1279
+ return result[0].json()
1209
1280
  } catch (e) {
1210
- return result.response
1281
+ return result
1211
1282
  }
1212
1283
  }
1213
1284
  }
@@ -1228,7 +1299,7 @@ export function xapiwrapper(ADL) {
1228
1299
  * ADL.XAPIWrapper.sendAgentProfile({"mbox":"mailto:tom@example.com"},
1229
1300
  * "agentprofile", profile, null, "*");
1230
1301
  */
1231
- XAPIWrapper.prototype.sendAgentProfile = function(
1302
+ XAPIWrapper.prototype.sendAgentProfile = function (
1232
1303
  agent,
1233
1304
  profileid,
1234
1305
  profileval,
@@ -1304,7 +1375,7 @@ export function xapiwrapper(ADL) {
1304
1375
  * function(r){ADL.XAPIWrapper.log(JSON.parse(r.response));});
1305
1376
  * >> {info: "the agent profile"}
1306
1377
  */
1307
- XAPIWrapper.prototype.getAgentProfile = function(
1378
+ XAPIWrapper.prototype.getAgentProfile = async function (
1308
1379
  agent,
1309
1380
  profileid,
1310
1381
  since,
@@ -1346,7 +1417,7 @@ export function xapiwrapper(ADL) {
1346
1417
  }
1347
1418
 
1348
1419
  try {
1349
- return JSON.parse(result.response)
1420
+ return result[0].json()
1350
1421
  } catch (e) {
1351
1422
  return result.response
1352
1423
  }
@@ -1368,7 +1439,7 @@ export function xapiwrapper(ADL) {
1368
1439
  * "agentprofile");
1369
1440
  * >> XMLHttpRequest {statusText: "NO CONTENT", status: 204, response: "", responseType: "", responseXML: null…}
1370
1441
  */
1371
- XAPIWrapper.prototype.deleteAgentProfile = function(
1442
+ XAPIWrapper.prototype.deleteAgentProfile = async function (
1372
1443
  agent,
1373
1444
  profileid,
1374
1445
  matchHash,
@@ -1410,7 +1481,7 @@ export function xapiwrapper(ADL) {
1410
1481
  }
1411
1482
 
1412
1483
  try {
1413
- return JSON.parse(result.response)
1484
+ return result[0].json()
1414
1485
  } catch (e) {
1415
1486
  return result
1416
1487
  }
@@ -1581,12 +1652,15 @@ export function xapiwrapper(ADL) {
1581
1652
  Copyright (c) 2010 Robert Kieffer
1582
1653
  Dual licensed under the MIT and GPL licenses.
1583
1654
  */
1584
- ADL.ruuid = function() {
1585
- return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(/[xy]/g, function(c) {
1586
- let r = (Math.random() * 16) | 0,
1587
- v = c == 'x' ? r : (r & 0x3) | 0x8
1588
- return v.toString(16)
1589
- })
1655
+ ADL.ruuid = function () {
1656
+ return 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx'.replace(
1657
+ /[xy]/g,
1658
+ function (c) {
1659
+ let r = (Math.random() * 16) | 0,
1660
+ v = c == 'x' ? r : (r & 0x3) | 0x8
1661
+ return v.toString(16)
1662
+ }
1663
+ )
1590
1664
  }
1591
1665
 
1592
1666
  /*
@@ -1594,7 +1668,7 @@ export function xapiwrapper(ADL) {
1594
1668
  * parses an ISO string into a date object
1595
1669
  * isostr - the ISO string
1596
1670
  */
1597
- ADL.dateFromISOString = function(isostr) {
1671
+ ADL.dateFromISOString = function (isostr) {
1598
1672
  let regexp =
1599
1673
  '([0-9]{4})(-([0-9]{2})(-([0-9]{2})' +
1600
1674
  '([T| ]([0-9]{2}):([0-9]{2})(:([0-9]{2})(.([0-9]+))?)?' +
@@ -1639,7 +1713,7 @@ export function xapiwrapper(ADL) {
1639
1713
  /*
1640
1714
  * makes a request to a server (if possible, use functions provided in XAPIWrapper)
1641
1715
  * @param {string} lrs the lrs connection info, such as endpoint, auth, etc
1642
- * @param {string} url the url of this request
1716
+ * @param {string | Array } urls the url of this request
1643
1717
  * @param {string} method the http request method
1644
1718
  * @param {string} data the payload
1645
1719
  * @param {string} auth the value for the Authorization header
@@ -1650,11 +1724,11 @@ export function xapiwrapper(ADL) {
1650
1724
  * @param {object} extraHeaders other header key-values to be added to this request
1651
1725
  * @param {boolean} withCredentials
1652
1726
  * @param {boolean} strictCallbacks Callback must be executed and first param is error or null if no error
1653
- * @return {object} xhr response object
1727
+ * @return {Objec} containing the status of last promise request and Array of the responses;
1654
1728
  */
1655
- ADL.XHR_request = function(
1729
+ ADL.XHR_request = function (
1656
1730
  lrs,
1657
- url,
1731
+ urls,
1658
1732
  method,
1659
1733
  data,
1660
1734
  auth,
@@ -1666,170 +1740,101 @@ export function xapiwrapper(ADL) {
1666
1740
  strictCallbacks
1667
1741
  ) {
1668
1742
  'use strict'
1743
+ let URLList = urls.constructor == Array ? [...urls] : [urls]
1744
+ // Consolidate headers
1745
+ let headers = {
1746
+ 'Content-Type': 'application/json',
1747
+ Authorization: auth,
1748
+ 'X-Experience-API-Version': ADL.XAPIWrapper.xapiVersion
1749
+ }
1669
1750
 
1670
- let xhr,
1671
- finished = false,
1672
- xDomainRequest = false,
1673
- ieXDomain = false,
1674
- ieModeRequest,
1675
- urlparts = url.toLowerCase().match(/^(.+):\/\/([^:/]*):?(\d+)?(\/.*)?$/),
1676
- location = window.location,
1677
- urlPort,
1678
- result,
1679
- extended,
1680
- prop,
1681
- until
1682
-
1683
- //Consolidate headers
1684
- let headers = {}
1685
- headers['Content-Type'] = 'application/json'
1686
- headers['Authorization'] = auth
1687
- headers['X-Experience-API-Version'] = ADL.XAPIWrapper.xapiVersion
1688
1751
  if (extraHeaders !== null) {
1689
1752
  for (let headerName in extraHeaders) {
1690
- if (extraHeaders.hasOwnProperty(headerName))
1753
+ if (extraHeaders.hasOwnProperty(headerName)) {
1691
1754
  headers[headerName] = extraHeaders[headerName]
1755
+ }
1692
1756
  }
1693
1757
  }
1694
1758
 
1695
- //See if this really is a cross domain
1696
- xDomainRequest =
1697
- location.protocol.toLowerCase() !== urlparts[1] ||
1698
- location.hostname.toLowerCase() !== urlparts[2]
1699
- if (!xDomainRequest) {
1700
- urlPort =
1701
- urlparts[3] === null
1702
- ? urlparts[1] === 'http'
1703
- ? '80'
1704
- : '443'
1705
- : urlparts[3]
1706
- xDomainRequest = urlPort === location.port
1707
- }
1708
-
1709
- //Add extended LMS-specified values to the URL
1759
+ // Add extended LMS-specified values to the URL
1710
1760
  if (lrs !== null && lrs.extended !== undefined) {
1711
- extended = new Array()
1712
- for (prop in lrs.extended) {
1761
+ let extended = []
1762
+ for (let prop in lrs.extended) {
1713
1763
  extended.push(prop + '=' + encodeURIComponent(lrs.extended[prop]))
1714
1764
  }
1715
1765
  if (extended.length > 0) {
1716
- url += (url.indexOf('?') > -1 ? '&' : '?') + extended.join('&')
1766
+ URLList.map(
1767
+ (url) =>
1768
+ (url += (url.indexOf('?') > -1 ? '&' : '?') + extended.join('&'))
1769
+ )
1717
1770
  }
1718
1771
  }
1772
+ let requestArray = []
1719
1773
 
1720
- //If it's not cross domain or we're not using IE, use the usual XmlHttpRequest
1721
- let windowsVersionCheck =
1722
- window.XDomainRequest &&
1723
- window.XMLHttpRequest &&
1724
- new XMLHttpRequest().responseType === undefined
1725
- if (
1726
- !xDomainRequest ||
1727
- windowsVersionCheck === undefined ||
1728
- windowsVersionCheck === false
1729
- ) {
1730
- xhr = new XMLHttpRequest()
1731
- xhr.withCredentials = withCredentials //allow cross domain cookie based auth
1732
- xhr.open(method, url, callback != null)
1733
- for (let headerName in headers) {
1734
- xhr.setRequestHeader(headerName, headers[headerName])
1774
+ for (const url of URLList) {
1775
+ // Prepare request options for fetch
1776
+ const fetchOptions = {
1777
+ method: method,
1778
+ headers: headers,
1779
+ body: data ? JSON.stringify(data) : null,
1780
+ credentials: withCredentials ? 'include' : 'same-origin'
1735
1781
  }
1736
- }
1737
- //Otherwise, use IE's XDomainRequest object
1738
- else {
1739
- ieXDomain = true
1740
- // ieModeRequest = ie_request(method, url, headers, data)
1741
- // xhr = new XDomainRequest()
1742
- xhr.open(ieModeRequest.method, ieModeRequest.url)
1782
+ requestArray.push(fetch(url, fetchOptions))
1743
1783
  }
1744
1784
 
1745
- //Setup request callback
1746
- function requestComplete() {
1747
- if (!finished) {
1748
- // may be in sync or async mode, using XMLHttpRequest or IE XDomainRequest, onreadystatechange or
1749
- // onload or both might fire depending upon browser, just covering all bases with event hooks and
1750
- // using 'finished' flag to avoid triggering events multiple times
1751
- finished = true
1752
- let notFoundOk = ignore404 && xhr.status === 404
1753
- if (
1754
- xhr.status === undefined ||
1755
- (xhr.status >= 200 && xhr.status < 400) ||
1756
- notFoundOk
1757
- ) {
1758
- if (callback) {
1759
- if (callbackargs) {
1760
- strictCallbacks
1761
- ? callback(null, xhr, callbackargs)
1762
- : callback(xhr, callbackargs)
1763
- } else {
1764
- let body
1765
-
1766
- try {
1767
- body = JSON.parse(xhr.responseText)
1768
- } catch (e) {
1769
- body = xhr.responseText
1770
- }
1785
+ // If no callback, we assume synchronous mode (using Promise)
1786
+ const makeRequest = async (requests) => {
1787
+ try {
1788
+ const response = await Promise.all(requests)
1789
+ const status = response[response.length - 1].status
1790
+ let body
1771
1791
 
1772
- strictCallbacks ? callback(null, xhr, body) : callback(xhr, body)
1773
- }
1774
- } else {
1775
- result = xhr
1776
- return xhr
1777
- }
1792
+ if (status === 404 && ignore404) {
1793
+ // If we are ignoring 404s
1794
+ body = null
1795
+ } else if (status >= 200 && status < 400) {
1796
+ // Successful response
1797
+ body = await response
1778
1798
  } else {
1779
- let warning
1780
- try {
1781
- warning =
1782
- 'There was a problem communicating with the Learning Record Store. ( ' +
1783
- xhr.status +
1784
- ' | ' +
1785
- xhr.response +
1786
- ' )' +
1787
- url
1788
- } catch (ex) {
1789
- warning = ex.toString()
1790
- }
1791
- ADL.XAPIWrapper.log(warning)
1792
- ADL.xhrRequestOnError(
1793
- xhr,
1794
- method,
1795
- url,
1796
- callback,
1797
- callbackargs,
1798
- strictCallbacks
1799
+ throw new Error(
1800
+ `There was a problem communicating with the Learning Record Store. (${status} | ${response.statusText}) ${urls}`
1799
1801
  )
1800
- result = xhr
1801
- return xhr
1802
1802
  }
1803
- } else {
1804
- return result
1805
- }
1806
- }
1807
1803
 
1808
- xhr.onreadystatechange = function() {
1809
- if (xhr.readyState === 4) {
1810
- return requestComplete()
1804
+ // Callback handling
1805
+ if (callback) {
1806
+ if (strictCallbacks) {
1807
+ callback(null, response, body, status)
1808
+ } else {
1809
+ callback(response, body, status)
1810
+ }
1811
+ }
1812
+ return { response, status }
1813
+ } catch (error) {
1814
+ // Log error to the console or call error handler
1815
+ ADL.XAPIWrapper.log(error.toString())
1816
+ if (callback) {
1817
+ if (strictCallbacks) {
1818
+ callback(error, null, callbackargs)
1819
+ } else {
1820
+ callback(null, callbackargs)
1821
+ }
1822
+ }
1823
+ return null
1811
1824
  }
1812
1825
  }
1813
1826
 
1814
- xhr.onload = requestComplete
1815
- xhr.onerror = requestComplete
1816
- //xhr.onerror = ADL.xhrRequestOnError(xhr, method, url);
1817
-
1818
- xhr.send(ieXDomain ? ieModeRequest.data : data)
1819
-
1820
1827
  if (!callback) {
1821
- // synchronous
1822
- if (ieXDomain) {
1823
- // synchronous call in IE, with no asynchronous mode available.
1824
- until = 1000 + new Date()
1825
- while (new Date() < until && xhr.readyState !== 4 && !finished) {
1826
- delay()
1827
- }
1828
- }
1829
- return requestComplete()
1828
+ // If no callback, we return the Promise (synchronous behavior)
1829
+ return makeRequest(requestArray)
1830
1830
  }
1831
+
1832
+ // If there's a callback, proceed with asynchronous request
1833
+ makeRequest(requestArray)
1831
1834
  }
1832
1835
 
1836
+ //====================================================================
1837
+
1833
1838
  /*
1834
1839
  * Holder for custom global error callback
1835
1840
  * @param {object} xhr xhr object or null
@@ -1845,7 +1850,7 @@ export function xapiwrapper(ADL) {
1845
1850
  * alert(xhr.status + " " + xhr.statusText + ": " + xhr.response);
1846
1851
  * };
1847
1852
  */
1848
- ADL.xhrRequestOnError = function(
1853
+ ADL.xhrRequestOnError = function (
1849
1854
  xhr,
1850
1855
  method,
1851
1856
  url,
@@ -1881,7 +1886,7 @@ export function xapiwrapper(ADL) {
1881
1886
  }
1882
1887
  }
1883
1888
 
1884
- ADL.formatHash = function(hash) {
1889
+ ADL.formatHash = function (hash) {
1885
1890
  return hash === '*' ? hash : '"' + hash + '"'
1886
1891
  }
1887
1892