@patch-adams/core 1.4.3 → 1.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -720,48 +720,55 @@ function generateLrsBridgeCode(options) {
720
720
 
721
721
  function setupBridge() {
722
722
  try {
723
- // 1. Try PlayerIntegration first (Xyleme native mode)
723
+ // 1. Try PlayerIntegration first (Xyleme native mode) - for SCORM APIs
724
724
  var integrationResult = findPlayerIntegration(10);
725
725
  if (integrationResult) {
726
726
  LRS.integration = integrationResult.integration;
727
727
  LRS.integrationLevel = integrationResult.level;
728
- LRS.mode = 'playerIntegration';
729
- log('Using PlayerIntegration mode');
728
+ log('Found PlayerIntegration at level', integrationResult.level);
730
729
 
731
730
  if (LRS.integration.Internal_SCORM_2004_API) {
732
731
  window.API_1484_11 = LRS.integration.Internal_SCORM_2004_API;
733
732
  LRS.scormApi = window.API_1484_11;
733
+ LRS.scormApiFound = true;
734
+ LRS.scormApiType = '2004';
734
735
  }
735
736
  if (LRS.integration.Internal_SCORM_12_API) {
736
737
  window.API = LRS.integration.Internal_SCORM_12_API;
738
+ if (!LRS.scormApiFound) {
739
+ LRS.scormApi = window.API;
740
+ LRS.scormApiFound = true;
741
+ LRS.scormApiType = '1.2';
742
+ }
737
743
  }
738
- return true;
739
- }
740
-
741
- // 2. Search for SCORM APIs
742
- log('No PlayerIntegration found, searching for SCORM APIs...');
743
-
744
- var api2004 = findAPIInFrameHierarchy('API_1484_11', 10);
745
- if (api2004) {
746
- LRS.scormApi = api2004.api;
747
- LRS.scormApiLevel = api2004.level;
748
- LRS.scormApiWindow = api2004.window;
749
- LRS.scormApiFound = true;
750
- LRS.scormApiType = '2004';
751
- log('Found SCORM 2004 API at level ' + api2004.level);
744
+ // Don't return early - continue to detect LRS endpoint for direct xAPI
752
745
  } else {
753
- var api12 = findAPIInFrameHierarchy('API', 10);
754
- if (api12) {
755
- LRS.scormApi = api12.api;
756
- LRS.scormApiLevel = api12.level;
757
- LRS.scormApiWindow = api12.window;
746
+ // 2. Search for SCORM APIs directly
747
+ log('No PlayerIntegration found, searching for SCORM APIs...');
748
+
749
+ var api2004 = findAPIInFrameHierarchy('API_1484_11', 10);
750
+ if (api2004) {
751
+ LRS.scormApi = api2004.api;
752
+ LRS.scormApiLevel = api2004.level;
753
+ LRS.scormApiWindow = api2004.window;
758
754
  LRS.scormApiFound = true;
759
- LRS.scormApiType = '1.2';
760
- log('Found SCORM 1.2 API at level ' + api12.level);
755
+ LRS.scormApiType = '2004';
756
+ log('Found SCORM 2004 API at level ' + api2004.level);
757
+ } else {
758
+ var api12 = findAPIInFrameHierarchy('API', 10);
759
+ if (api12) {
760
+ LRS.scormApi = api12.api;
761
+ LRS.scormApiLevel = api12.level;
762
+ LRS.scormApiWindow = api12.window;
763
+ LRS.scormApiFound = true;
764
+ LRS.scormApiType = '1.2';
765
+ log('Found SCORM 1.2 API at level ' + api12.level);
766
+ }
761
767
  }
762
768
  }
763
769
 
764
- // 3. Determine LRS endpoint
770
+ // 3. ALWAYS try to determine LRS endpoint for direct xAPI statements
771
+ // This is critical for detailed tracking (media, interactions, quizzes)
765
772
  // Priority: 1) Configured endpoint, 2) Auto-detected Bravais endpoint
766
773
  if (LRS_ENDPOINT_CONFIG && LRS_ENDPOINT_CONFIG.length > 0) {
767
774
  LRS_ENDPOINT = LRS_ENDPOINT_CONFIG;
@@ -777,9 +784,17 @@ function generateLrsBridgeCode(options) {
777
784
  LRS.lrsEndpoint = LRS_ENDPOINT;
778
785
 
779
786
  // 4. Determine mode based on what we found
787
+ // Prefer directLRS when available for detailed tracking
780
788
  if (LRS_ENDPOINT && LRS_ENDPOINT.length > 0) {
781
789
  LRS.mode = 'directLRS';
782
790
  log('Using direct LRS mode with endpoint:', LRS_ENDPOINT);
791
+ if (LRS.integration) {
792
+ log('PlayerIntegration also available for SCORM state');
793
+ }
794
+ return true;
795
+ } else if (LRS.integration) {
796
+ LRS.mode = 'playerIntegration';
797
+ log('Using PlayerIntegration mode (no direct LRS endpoint)');
783
798
  return true;
784
799
  } else if (LRS.scormApiFound) {
785
800
  LRS.mode = 'scormOnly';
@@ -1594,7 +1609,7 @@ function generateLrsBridgeCode(options) {
1594
1609
  } catch (e) {}
1595
1610
 
1596
1611
  for (var j = 0; j < urlsToCheck.length; j++) {
1597
- var match = urlsToCheck[j].match(/(https?://core-[a-zA-Z0-9_-]+.bravais.com)/);
1612
+ var match = urlsToCheck[j].match(/(https?:\\/\\/core-[a-zA-Z0-9_-]+\\.bravais\\.com)/);
1598
1613
  if (match) {
1599
1614
  return match[1];
1600
1615
  }
@@ -1998,6 +2013,154 @@ function generateLrsBridgeCode(options) {
1998
2013
  return info;
1999
2014
  }
2000
2015
 
2016
+ // ========================================================================
2017
+ // 4b. LESSON/SECTION NAME EXTRACTION
2018
+ // ========================================================================
2019
+
2020
+ /**
2021
+ * Get the current lesson/section information from Rise DOM
2022
+ * Returns { id, name, lessonIndex, sectionName }
2023
+ */
2024
+ function getCurrentLessonInfo() {
2025
+ var lessonInfo = {
2026
+ id: window.location.hash || '#/',
2027
+ name: null,
2028
+ lessonIndex: null,
2029
+ sectionName: null
2030
+ };
2031
+
2032
+ try {
2033
+ // 1. Try to get from Rise's navigation sidebar
2034
+ // Rise shows active lesson with specific classes
2035
+ var activeLesson = document.querySelector(
2036
+ '.sidebar__link--current, ' +
2037
+ '.sidebar-lesson--active, ' +
2038
+ '.nav-sidebar__lesson--active, ' +
2039
+ '[class*="sidebar"] [class*="current"], ' +
2040
+ '[class*="sidebar"] [class*="active"], ' +
2041
+ '[aria-current="page"], ' +
2042
+ '[aria-current="true"]'
2043
+ );
2044
+
2045
+ if (activeLesson) {
2046
+ var lessonTitle = activeLesson.querySelector(
2047
+ '.sidebar__link-text, ' +
2048
+ '.lesson-title, ' +
2049
+ '.sidebar-lesson__title, ' +
2050
+ '[class*="title"], ' +
2051
+ '[class*="label"]'
2052
+ );
2053
+ if (lessonTitle) {
2054
+ lessonInfo.name = lessonTitle.textContent.trim();
2055
+ } else {
2056
+ lessonInfo.name = activeLesson.textContent.trim();
2057
+ }
2058
+
2059
+ // Try to get the section/module name (parent group)
2060
+ var section = activeLesson.closest(
2061
+ '.sidebar__section, ' +
2062
+ '.sidebar-section, ' +
2063
+ '[class*="section"], ' +
2064
+ '[class*="module"]'
2065
+ );
2066
+ if (section) {
2067
+ var sectionTitle = section.querySelector(
2068
+ '.sidebar__section-title, ' +
2069
+ '.section-title, ' +
2070
+ '[class*="section-title"], ' +
2071
+ '[class*="module-title"], ' +
2072
+ 'h2, h3'
2073
+ );
2074
+ if (sectionTitle) {
2075
+ lessonInfo.sectionName = sectionTitle.textContent.trim();
2076
+ }
2077
+ }
2078
+ }
2079
+
2080
+ // 2. Try Rise's header/breadcrumb
2081
+ if (!lessonInfo.name) {
2082
+ var headerTitle = document.querySelector(
2083
+ '.lesson-header__title, ' +
2084
+ '.lesson__title, ' +
2085
+ '.content-header__title, ' +
2086
+ '[class*="lesson"] [class*="header"] [class*="title"], ' +
2087
+ '.blocks-lesson-header__title'
2088
+ );
2089
+ if (headerTitle) {
2090
+ lessonInfo.name = headerTitle.textContent.trim();
2091
+ }
2092
+ }
2093
+
2094
+ // 3. Try breadcrumbs
2095
+ if (!lessonInfo.name || !lessonInfo.sectionName) {
2096
+ var breadcrumbs = document.querySelectorAll(
2097
+ '.breadcrumb__item, ' +
2098
+ '.breadcrumbs li, ' +
2099
+ '[class*="breadcrumb"] span, ' +
2100
+ '[class*="breadcrumb"] a'
2101
+ );
2102
+ if (breadcrumbs.length >= 2) {
2103
+ // Usually: Course > Section > Lesson
2104
+ if (!lessonInfo.sectionName && breadcrumbs.length >= 2) {
2105
+ lessonInfo.sectionName = breadcrumbs[breadcrumbs.length - 2].textContent.trim();
2106
+ }
2107
+ if (!lessonInfo.name) {
2108
+ lessonInfo.name = breadcrumbs[breadcrumbs.length - 1].textContent.trim();
2109
+ }
2110
+ }
2111
+ }
2112
+
2113
+ // 4. Try Rise's internal data
2114
+ if (window.__RISE_COURSE_DATA__ && window.__RISE_COURSE_DATA__.lessons) {
2115
+ var lessonId = lessonInfo.id.replace('#/lessons/', '').replace('#/', '');
2116
+ var lessons = window.__RISE_COURSE_DATA__.lessons;
2117
+ for (var i = 0; i < lessons.length; i++) {
2118
+ if (lessons[i].id === lessonId || lessons[i].slug === lessonId) {
2119
+ lessonInfo.name = lessons[i].title || lessonInfo.name;
2120
+ lessonInfo.lessonIndex = i;
2121
+ break;
2122
+ }
2123
+ }
2124
+ }
2125
+
2126
+ // 5. Clean up - remove any "Home" or generic names if we're on a real lesson
2127
+ if (lessonInfo.name) {
2128
+ lessonInfo.name = lessonInfo.name.replace(/^\\\\d+\\\\.\\\\s*/, ''); // Remove leading numbers "1. "
2129
+ if (lessonInfo.name.length > 200) {
2130
+ lessonInfo.name = lessonInfo.name.substring(0, 200) + '...';
2131
+ }
2132
+ }
2133
+
2134
+ if (lessonInfo.sectionName) {
2135
+ lessonInfo.sectionName = lessonInfo.sectionName.replace(/^\\\\d+\\\\.\\\\s*/, '');
2136
+ if (lessonInfo.sectionName.length > 200) {
2137
+ lessonInfo.sectionName = lessonInfo.sectionName.substring(0, 200) + '...';
2138
+ }
2139
+ }
2140
+
2141
+ } catch (e) {
2142
+ warn('Error extracting lesson info:', e);
2143
+ }
2144
+
2145
+ log('Current lesson info:', lessonInfo);
2146
+ return lessonInfo;
2147
+ }
2148
+
2149
+ /**
2150
+ * Cache the current lesson info (updates on navigation)
2151
+ */
2152
+ var cachedLessonInfo = null;
2153
+ var cachedLessonHash = null;
2154
+
2155
+ function getCachedLessonInfo() {
2156
+ var currentHash = window.location.hash;
2157
+ if (cachedLessonHash !== currentHash) {
2158
+ cachedLessonInfo = getCurrentLessonInfo();
2159
+ cachedLessonHash = currentHash;
2160
+ }
2161
+ return cachedLessonInfo || getCurrentLessonInfo();
2162
+ }
2163
+
2001
2164
  // ========================================================================
2002
2165
  // 5. XAPI STATEMENT BUILDING
2003
2166
  // ========================================================================
@@ -2342,14 +2505,20 @@ function generateLrsBridgeCode(options) {
2342
2505
  LRS.eventLog.push({
2343
2506
  statement: statement,
2344
2507
  timestamp: new Date().toISOString(),
2345
- mode: LRS.mode
2508
+ mode: LRS.mode,
2509
+ lrsEndpoint: LRS_ENDPOINT
2346
2510
  });
2347
2511
 
2348
- // Route based on mode
2349
- if (LRS.mode === 'playerIntegration' && LRS.integration) {
2350
- return sendViaPlayerIntegration(statement);
2351
- } else if (LRS.mode === 'directLRS' && LRS_ENDPOINT) {
2512
+ // ALWAYS prefer direct LRS when endpoint is available
2513
+ // This ensures all our detailed tracking (media, interactions, etc.) goes to the LRS
2514
+ // PlayerIntegration is used only for SCORM state, not for xAPI statements
2515
+ if (LRS_ENDPOINT && LRS_ENDPOINT.length > 0) {
2516
+ log('Sending via directLRS:', LRS_ENDPOINT);
2352
2517
  return sendViaDirectLRS(statement);
2518
+ } else if (LRS.mode === 'playerIntegration' && LRS.integration) {
2519
+ // Fallback to PlayerIntegration only if no direct LRS endpoint
2520
+ log('No LRS endpoint, falling back to PlayerIntegration');
2521
+ return sendViaPlayerIntegration(statement);
2353
2522
  } else {
2354
2523
  // SCORM-only or offline mode - just log
2355
2524
  log('Statement logged (no LRS):', statement.verb.display['en-US']);
@@ -2488,13 +2657,16 @@ function generateLrsBridgeCode(options) {
2488
2657
  LRS.contentOpened = function(data) {
2489
2658
  if (!TRACK_NAVIGATION) return;
2490
2659
 
2660
+ // Get current lesson info from DOM (includes name)
2661
+ var lessonInfo = getCurrentLessonInfo();
2662
+
2491
2663
  var result = null;
2492
2664
  if (data.duration) {
2493
2665
  result = { duration: data.duration };
2494
2666
  }
2495
2667
 
2496
2668
  // Extract clean page/lesson ID from Rise hash paths
2497
- var lessonId = data.pageGuid || data.lessonId || '';
2669
+ var lessonId = data.pageGuid || data.lessonId || lessonInfo.id || '';
2498
2670
  if (lessonId.indexOf('#/lessons/') === 0) {
2499
2671
  lessonId = lessonId.substring(10); // Remove '#/lessons/'
2500
2672
  } else if (lessonId.indexOf('#/') === 0) {
@@ -2505,7 +2677,9 @@ function generateLrsBridgeCode(options) {
2505
2677
  var additionalContext = {
2506
2678
  extensions: {
2507
2679
  pageId: lessonId || 'home',
2508
- pageTitle: data.title || 'Page',
2680
+ pageTitle: data.title || lessonInfo.name || 'Page',
2681
+ lessonName: lessonInfo.name,
2682
+ sectionName: lessonInfo.sectionName,
2509
2683
  pageUrl: data.url || window.location.href
2510
2684
  }
2511
2685
  };
@@ -2515,7 +2689,12 @@ function generateLrsBridgeCode(options) {
2515
2689
  var statement = buildStatement(
2516
2690
  'experienced',
2517
2691
  'http://xyleme.com/bravais/activities/document',
2518
- { event: 'page_viewed', pageId: lessonId },
2692
+ {
2693
+ event: 'page_viewed',
2694
+ pageId: lessonId,
2695
+ lessonName: lessonInfo.name,
2696
+ sectionName: lessonInfo.sectionName
2697
+ },
2519
2698
  result,
2520
2699
  additionalContext
2521
2700
  );
@@ -2526,6 +2705,9 @@ function generateLrsBridgeCode(options) {
2526
2705
  LRS.mediaPlayed = function(data) {
2527
2706
  if (!TRACK_MEDIA) return;
2528
2707
 
2708
+ // Get current lesson context
2709
+ var lessonInfo = getCachedLessonInfo();
2710
+
2529
2711
  var mediaType = data.type === 'audio' ? ACTIVITY_TYPES.audio : ACTIVITY_TYPES.video;
2530
2712
  var verbKey = data.action === 'play' ? 'played' :
2531
2713
  data.action === 'pause' ? 'paused' :
@@ -2536,7 +2718,11 @@ function generateLrsBridgeCode(options) {
2536
2718
  mediaSrc: data.src || 'media-' + Date.now(),
2537
2719
  mediaName: data.name || (data.type || 'Media'),
2538
2720
  mediaType: data.type || 'video',
2539
- 'https://w3id.org/xapi/video/extensions/length': data.duration || 0
2721
+ 'https://w3id.org/xapi/video/extensions/length': data.duration || 0,
2722
+ // Include lesson/section context
2723
+ lessonId: lessonInfo.id,
2724
+ lessonName: lessonInfo.name,
2725
+ sectionName: lessonInfo.sectionName
2540
2726
  };
2541
2727
 
2542
2728
  var result = {
@@ -2559,12 +2745,21 @@ function generateLrsBridgeCode(options) {
2559
2745
  LRS.assessmentStarted = function(data) {
2560
2746
  if (!TRACK_QUIZZES) return;
2561
2747
 
2748
+ // Get lesson context
2749
+ var lessonInfo = getCachedLessonInfo();
2750
+
2562
2751
  // Build assessment activity object for Xyleme format
2563
2752
  var assessmentObject = buildAssessmentActivityObject({
2564
2753
  assessmentGuid: data.assessmentGuid || data.id,
2565
- title: data.title || 'Assessment'
2754
+ title: data.title || data.assessmentName || 'Assessment'
2566
2755
  });
2567
2756
 
2757
+ // Add lesson context to extensions
2758
+ assessmentObject.definition = assessmentObject.definition || {};
2759
+ assessmentObject.definition.extensions = assessmentObject.definition.extensions || {};
2760
+ assessmentObject.definition.extensions.lessonName = lessonInfo.name;
2761
+ assessmentObject.definition.extensions.sectionName = lessonInfo.sectionName;
2762
+
2568
2763
  var statement = buildStatementXyleme('attempted', assessmentObject);
2569
2764
  sendStatement(statement);
2570
2765
  };
@@ -2573,13 +2768,31 @@ function generateLrsBridgeCode(options) {
2573
2768
  LRS.assessmentEnded = function(data) {
2574
2769
  if (!TRACK_QUIZZES) return;
2575
2770
 
2771
+ // Get lesson context (may be passed in from extractQuizResult)
2772
+ var lessonInfo = data.lessonName ? { name: data.lessonName, sectionName: data.sectionName } : getCachedLessonInfo();
2773
+
2576
2774
  // Build assessment activity object for Xyleme format
2577
2775
  var assessmentObject = buildAssessmentActivityObject({
2578
- assessmentGuid: data.assessmentGuid || data.id,
2579
- title: data.title || 'Assessment'
2776
+ assessmentGuid: data.assessmentGuid || data.assessmentId || data.id,
2777
+ title: data.title || data.assessmentName || 'Assessment'
2580
2778
  });
2581
2779
 
2582
- var result = { completion: true };
2780
+ // Add lesson context to extensions
2781
+ assessmentObject.definition = assessmentObject.definition || {};
2782
+ assessmentObject.definition.extensions = assessmentObject.definition.extensions || {};
2783
+ assessmentObject.definition.extensions.lessonName = lessonInfo.name;
2784
+ assessmentObject.definition.extensions.sectionName = lessonInfo.sectionName;
2785
+ assessmentObject.definition.extensions.assessmentName = data.assessmentName || data.title;
2786
+
2787
+ var result = {
2788
+ completion: true,
2789
+ extensions: {
2790
+ 'https://patch-adams.io/xapi/extensions/assessmentName': data.assessmentName || data.title || null,
2791
+ 'https://patch-adams.io/xapi/extensions/lessonName': lessonInfo.name || null,
2792
+ 'https://patch-adams.io/xapi/extensions/sectionName': lessonInfo.sectionName || null,
2793
+ 'https://patch-adams.io/xapi/extensions/questionCount': data.questionCount || null
2794
+ }
2795
+ };
2583
2796
 
2584
2797
  if (typeof data.score === 'number') {
2585
2798
  result.score = {
@@ -2608,14 +2821,27 @@ function generateLrsBridgeCode(options) {
2608
2821
  LRS.questionAnswered = function(data) {
2609
2822
  if (!TRACK_QUIZZES) return;
2610
2823
 
2824
+ // Get lesson context if not provided
2825
+ var lessonInfo = data.lessonName ? data : getCachedLessonInfo();
2826
+
2611
2827
  // Build question activity object for Xyleme format
2612
2828
  var questionObject = buildQuestionActivityObject({
2613
2829
  questionGuid: data.questionGuid || data.questionId,
2614
2830
  text: data.questionText || 'Question'
2615
2831
  });
2616
2832
 
2833
+ // Build comprehensive result with human-readable data
2617
2834
  var result = {
2618
- response: data.answer || data.response || ''
2835
+ response: data.answer || data.response || '',
2836
+ extensions: {
2837
+ 'https://patch-adams.io/xapi/extensions/questionNumber': data.questionNumber || null,
2838
+ 'https://patch-adams.io/xapi/extensions/questionText': data.questionText || null,
2839
+ 'https://patch-adams.io/xapi/extensions/answerText': data.answer || data.response || null,
2840
+ 'https://patch-adams.io/xapi/extensions/correctAnswer': data.correctAnswer || null,
2841
+ 'https://patch-adams.io/xapi/extensions/assessmentName': data.assessmentName || null,
2842
+ 'https://patch-adams.io/xapi/extensions/lessonName': lessonInfo.lessonName || lessonInfo.name || null,
2843
+ 'https://patch-adams.io/xapi/extensions/sectionName': lessonInfo.sectionName || null
2844
+ }
2619
2845
  };
2620
2846
 
2621
2847
  if (data.result === 'correct' || data.correct === true) {
@@ -2647,6 +2873,9 @@ function generateLrsBridgeCode(options) {
2647
2873
  LRS.interacted = function(data) {
2648
2874
  if (!TRACK_INTERACTIONS) return;
2649
2875
 
2876
+ // Get current lesson context
2877
+ var lessonInfo = getCachedLessonInfo();
2878
+
2650
2879
  // Activity type is interaction, details include interaction ID and type
2651
2880
  var statement = buildStatement(
2652
2881
  'interacted',
@@ -2654,7 +2883,11 @@ function generateLrsBridgeCode(options) {
2654
2883
  {
2655
2884
  interactionId: data.id || 'interaction-' + Date.now(),
2656
2885
  interactionName: data.name || data.type || 'Interaction',
2657
- interactionType: data.interactionType || data.type || 'other'
2886
+ interactionType: data.interactionType || data.type || 'other',
2887
+ // Include lesson/section context
2888
+ lessonId: lessonInfo.id,
2889
+ lessonName: lessonInfo.name,
2890
+ sectionName: lessonInfo.sectionName
2658
2891
  }
2659
2892
  );
2660
2893
  sendStatement(statement);
@@ -2955,43 +3188,152 @@ function generateLrsBridgeCode(options) {
2955
3188
  'quiz-' + Date.now();
2956
3189
  }
2957
3190
 
3191
+ /**
3192
+ * Extract quiz/assessment name from the DOM
3193
+ */
3194
+ function extractAssessmentName(node) {
3195
+ // Try various selectors for assessment/quiz titles
3196
+ var titleEl = node.querySelector(
3197
+ '.quiz-title, .assessment-title, .knowledge-check-title, ' +
3198
+ '.blocks-quiz__title, .block-title, ' +
3199
+ '[class*="quiz"] [class*="title"], ' +
3200
+ '[class*="assessment"] [class*="title"], ' +
3201
+ 'h2, h3'
3202
+ );
3203
+ if (titleEl) {
3204
+ return titleEl.textContent.trim().substring(0, 200);
3205
+ }
3206
+
3207
+ // Try to get from parent block
3208
+ var parentBlock = node.closest('[data-block-type]');
3209
+ if (parentBlock) {
3210
+ var blockTitle = parentBlock.querySelector('.block-title, h2, h3');
3211
+ if (blockTitle) {
3212
+ return blockTitle.textContent.trim().substring(0, 200);
3213
+ }
3214
+ }
3215
+
3216
+ // Get current lesson name as context
3217
+ var lessonInfo = getCachedLessonInfo();
3218
+ if (lessonInfo.name) {
3219
+ return 'Quiz in ' + lessonInfo.name;
3220
+ }
3221
+
3222
+ return 'Knowledge Check';
3223
+ }
3224
+
2958
3225
  function extractQuizResult(node) {
2959
3226
  var scoreEl = node.querySelector('.score-value, .quiz-score, .score-percentage, [class*="score"]');
2960
3227
  var score = scoreEl ? parseFloat(scoreEl.textContent.replace(/[^0-9.]/g, '')) : null;
2961
3228
 
2962
- var questions = node.querySelectorAll('.question-result, .question-feedback, .question-item, [class*="question"]');
3229
+ // Get assessment name and lesson context
3230
+ var assessmentName = extractAssessmentName(node);
3231
+ var lessonInfo = getCachedLessonInfo();
3232
+
3233
+ // Find all question containers - Rise uses various structures
3234
+ var questions = node.querySelectorAll(
3235
+ '.question-result, .question-feedback, .question-item, ' +
3236
+ '.blocks-quiz__question, .quiz-question, ' +
3237
+ '[class*="question-"][class*="result"], ' +
3238
+ '[class*="question-"][class*="feedback"], ' +
3239
+ '[data-question-id]'
3240
+ );
3241
+
3242
+ log('Found', questions.length, 'question elements in quiz result');
2963
3243
 
2964
3244
  if (questions.length > 0) {
2965
3245
  questions.forEach(function(q, index) {
3246
+ // Determine if correct - check multiple indicators
2966
3247
  var isCorrect = q.classList.contains('correct') ||
2967
- q.querySelector('.correct, [class*="correct"]') !== null ||
2968
- q.getAttribute('data-correct') === 'true';
3248
+ q.classList.contains('is-correct') ||
3249
+ q.querySelector('.correct, .is-correct, [class*="correct"]') !== null ||
3250
+ q.getAttribute('data-correct') === 'true' ||
3251
+ q.getAttribute('data-result') === 'correct';
2969
3252
 
3253
+ // Get the question text - try multiple selectors
2970
3254
  var questionText = '';
2971
- var questionEl = q.querySelector('.question-text, .question-stem, .question-title, [class*="question-text"]');
3255
+ var questionEl = q.querySelector(
3256
+ '.question-text, .question-stem, .question-title, ' +
3257
+ '.blocks-quiz__question-text, .quiz-question__text, ' +
3258
+ '[class*="question-text"], [class*="question-stem"], ' +
3259
+ '[class*="prompt"], p:first-of-type'
3260
+ );
2972
3261
  if (questionEl) {
2973
3262
  questionText = questionEl.textContent.trim();
2974
3263
  }
2975
3264
 
3265
+ // Get the learner's selected answer text
2976
3266
  var answerText = '';
2977
- var answerEl = q.querySelector('.selected-answer, .learner-response, .answer-text, [class*="response"]');
3267
+ var answerEl = q.querySelector(
3268
+ '.selected-answer, .learner-response, .answer-text, ' +
3269
+ '.user-answer, .chosen-answer, .response-text, ' +
3270
+ '.blocks-quiz__response, .quiz-question__response, ' +
3271
+ '[class*="selected"], [class*="chosen"], [class*="response"], ' +
3272
+ '[class*="user-answer"]'
3273
+ );
2978
3274
  if (answerEl) {
2979
3275
  answerText = answerEl.textContent.trim();
2980
3276
  }
2981
3277
 
3278
+ // If no specific answer element, try to find selected radio/checkbox label
3279
+ if (!answerText) {
3280
+ var selectedInput = q.querySelector('input:checked, [aria-checked="true"]');
3281
+ if (selectedInput) {
3282
+ var label = q.querySelector('label[for="' + selectedInput.id + '"]');
3283
+ if (label) {
3284
+ answerText = label.textContent.trim();
3285
+ } else {
3286
+ var parentLabel = selectedInput.closest('label');
3287
+ if (parentLabel) {
3288
+ answerText = parentLabel.textContent.trim();
3289
+ }
3290
+ }
3291
+ }
3292
+ }
3293
+
3294
+ // Get question ID if available
3295
+ var questionId = q.getAttribute('data-question-id') ||
3296
+ q.getAttribute('data-block-id') ||
3297
+ q.getAttribute('id') ||
3298
+ 'q' + (index + 1);
3299
+
3300
+ // Get correct answer text if available (for reporting)
3301
+ var correctAnswerText = '';
3302
+ var correctEl = q.querySelector(
3303
+ '.correct-answer, .right-answer, ' +
3304
+ '[class*="correct-answer"], [class*="right-answer"]'
3305
+ );
3306
+ if (correctEl) {
3307
+ correctAnswerText = correctEl.textContent.trim();
3308
+ }
3309
+
3310
+ log('Question', index + 1, ':', {
3311
+ questionText: questionText.substring(0, 50) + '...',
3312
+ answerText: answerText.substring(0, 50) + '...',
3313
+ isCorrect: isCorrect
3314
+ });
3315
+
2982
3316
  LRS.questionAnswered({
2983
- questionId: 'q' + (index + 1),
2984
- questionText: questionText.substring(0, 200),
2985
- answer: answerText.substring(0, 200),
2986
- result: isCorrect ? 'correct' : 'incorrect'
3317
+ questionId: questionId,
3318
+ questionNumber: index + 1,
3319
+ questionText: questionText.substring(0, 500),
3320
+ answer: answerText.substring(0, 500),
3321
+ correctAnswer: correctAnswerText.substring(0, 500),
3322
+ result: isCorrect ? 'correct' : 'incorrect',
3323
+ assessmentName: assessmentName,
3324
+ lessonName: lessonInfo.name,
3325
+ sectionName: lessonInfo.sectionName
2987
3326
  });
2988
3327
  });
2989
3328
  }
2990
3329
 
2991
3330
  LRS.assessmentEnded({
2992
3331
  assessmentId: extractAssessmentId(node),
3332
+ assessmentName: assessmentName,
2993
3333
  score: score,
2994
- questionCount: questions.length
3334
+ questionCount: questions.length,
3335
+ lessonName: lessonInfo.name,
3336
+ sectionName: lessonInfo.sectionName
2995
3337
  });
2996
3338
  }
2997
3339
 
@@ -3115,7 +3457,7 @@ function generateLrsBridgeCode(options) {
3115
3457
  // ========================================================================
3116
3458
 
3117
3459
  function init() {
3118
- log('Initializing LRS bridge v2.3.0...');
3460
+ log('Initializing LRS bridge v2.5.1...');
3119
3461
 
3120
3462
  // Extract course info early
3121
3463
  extractCourseInfo();