@patch-adams/core 1.4.0 → 1.4.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli.cjs +283 -54
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +283 -54
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +283 -54
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +283 -54
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1900,24 +1900,12 @@ function generateLrsBridgeCode(options) {
|
|
|
1900
1900
|
// Store for document API data
|
|
1901
1901
|
var documentApiData = null;
|
|
1902
1902
|
var documentApiFetched = false;
|
|
1903
|
+
var sharedLinkApiData = null;
|
|
1903
1904
|
|
|
1904
1905
|
/**
|
|
1905
|
-
*
|
|
1906
|
-
* Endpoint: /api/v3/documents/{documentId}
|
|
1906
|
+
* Detect Bravais core URL from current page context
|
|
1907
1907
|
*/
|
|
1908
|
-
function
|
|
1909
|
-
if (!documentId) {
|
|
1910
|
-
callback(null);
|
|
1911
|
-
return;
|
|
1912
|
-
}
|
|
1913
|
-
|
|
1914
|
-
if (documentApiFetched) {
|
|
1915
|
-
callback(documentApiData);
|
|
1916
|
-
return;
|
|
1917
|
-
}
|
|
1918
|
-
|
|
1919
|
-
// Detect Bravais core URL
|
|
1920
|
-
var coreUrl = null;
|
|
1908
|
+
function detectCoreUrl() {
|
|
1921
1909
|
var urlsToCheck = [window.location.href, document.referrer];
|
|
1922
1910
|
|
|
1923
1911
|
try {
|
|
@@ -1933,13 +1921,91 @@ function generateLrsBridgeCode(options) {
|
|
|
1933
1921
|
} catch (e) {}
|
|
1934
1922
|
|
|
1935
1923
|
for (var j = 0; j < urlsToCheck.length; j++) {
|
|
1936
|
-
var match = urlsToCheck[j].match(/(https
|
|
1924
|
+
var match = urlsToCheck[j].match(/(https?://core-[a-zA-Z0-9_-]+.bravais.com)/);
|
|
1937
1925
|
if (match) {
|
|
1938
|
-
|
|
1939
|
-
break;
|
|
1926
|
+
return match[1];
|
|
1940
1927
|
}
|
|
1941
1928
|
}
|
|
1929
|
+
return null;
|
|
1930
|
+
}
|
|
1931
|
+
|
|
1932
|
+
/**
|
|
1933
|
+
* Fetch shared link data from Bravais API to get the real document ID
|
|
1934
|
+
* Endpoint: /api/v3/sharedLinks/token/{token}
|
|
1935
|
+
* Returns: { documentId, documentName, format, ... }
|
|
1936
|
+
*/
|
|
1937
|
+
function fetchSharedLinkData(token, callback) {
|
|
1938
|
+
if (!token) {
|
|
1939
|
+
callback(null);
|
|
1940
|
+
return;
|
|
1941
|
+
}
|
|
1942
1942
|
|
|
1943
|
+
var coreUrl = detectCoreUrl();
|
|
1944
|
+
if (!coreUrl) {
|
|
1945
|
+
log('No Bravais core URL detected for shared link fetch');
|
|
1946
|
+
callback(null);
|
|
1947
|
+
return;
|
|
1948
|
+
}
|
|
1949
|
+
|
|
1950
|
+
var apiUrl = coreUrl + '/api/v3/sharedLinks/token/' + token;
|
|
1951
|
+
log('Fetching shared link data from:', apiUrl);
|
|
1952
|
+
|
|
1953
|
+
var xhr = new XMLHttpRequest();
|
|
1954
|
+
xhr.open('GET', apiUrl, true);
|
|
1955
|
+
xhr.withCredentials = true;
|
|
1956
|
+
xhr.setRequestHeader('Accept', 'application/json');
|
|
1957
|
+
|
|
1958
|
+
xhr.onreadystatechange = function() {
|
|
1959
|
+
if (xhr.readyState === 4) {
|
|
1960
|
+
log('Shared link API response status:', xhr.status);
|
|
1961
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
1962
|
+
try {
|
|
1963
|
+
sharedLinkApiData = JSON.parse(xhr.responseText);
|
|
1964
|
+
log('Shared link data received:', sharedLinkApiData);
|
|
1965
|
+
if (sharedLinkApiData.documentId) {
|
|
1966
|
+
log('Real document ID from shared link:', sharedLinkApiData.documentId);
|
|
1967
|
+
}
|
|
1968
|
+
callback(sharedLinkApiData);
|
|
1969
|
+
} catch (e) {
|
|
1970
|
+
warn('Error parsing shared link data:', e);
|
|
1971
|
+
callback(null);
|
|
1972
|
+
}
|
|
1973
|
+
} else {
|
|
1974
|
+
warn('Shared link fetch failed. Status:', xhr.status);
|
|
1975
|
+
callback(null);
|
|
1976
|
+
}
|
|
1977
|
+
}
|
|
1978
|
+
};
|
|
1979
|
+
|
|
1980
|
+
xhr.onerror = function() {
|
|
1981
|
+
log('Network error fetching shared link data');
|
|
1982
|
+
callback(null);
|
|
1983
|
+
};
|
|
1984
|
+
|
|
1985
|
+
try {
|
|
1986
|
+
xhr.send();
|
|
1987
|
+
} catch (e) {
|
|
1988
|
+
warn('Error sending shared link request:', e);
|
|
1989
|
+
callback(null);
|
|
1990
|
+
}
|
|
1991
|
+
}
|
|
1992
|
+
|
|
1993
|
+
/**
|
|
1994
|
+
* Fetch document metadata from Bravais API to get GUIDs
|
|
1995
|
+
* Endpoint: /api/v3/documents/{documentId}
|
|
1996
|
+
*/
|
|
1997
|
+
function fetchDocumentMetadata(documentId, callback) {
|
|
1998
|
+
if (!documentId) {
|
|
1999
|
+
callback(null);
|
|
2000
|
+
return;
|
|
2001
|
+
}
|
|
2002
|
+
|
|
2003
|
+
if (documentApiFetched) {
|
|
2004
|
+
callback(documentApiData);
|
|
2005
|
+
return;
|
|
2006
|
+
}
|
|
2007
|
+
|
|
2008
|
+
var coreUrl = detectCoreUrl();
|
|
1943
2009
|
if (!coreUrl) {
|
|
1944
2010
|
log('No Bravais core URL detected for document metadata fetch');
|
|
1945
2011
|
documentApiFetched = true;
|
|
@@ -1963,13 +2029,24 @@ function generateLrsBridgeCode(options) {
|
|
|
1963
2029
|
try {
|
|
1964
2030
|
documentApiData = JSON.parse(xhr.responseText);
|
|
1965
2031
|
log('Document metadata received:', documentApiData);
|
|
2032
|
+
// Validate that we got the required fields
|
|
2033
|
+
if (documentApiData.guid) {
|
|
2034
|
+
log('Document GUID found:', documentApiData.guid);
|
|
2035
|
+
} else {
|
|
2036
|
+
warn('Document API response missing guid field!', documentApiData);
|
|
2037
|
+
}
|
|
2038
|
+
if (documentApiData.latestVersion && documentApiData.latestVersion.guid) {
|
|
2039
|
+
log('Version GUID found:', documentApiData.latestVersion.guid);
|
|
2040
|
+
} else {
|
|
2041
|
+
warn('Document API response missing latestVersion.guid field!', documentApiData);
|
|
2042
|
+
}
|
|
1966
2043
|
callback(documentApiData);
|
|
1967
2044
|
} catch (e) {
|
|
1968
2045
|
warn('Error parsing document metadata:', e);
|
|
1969
2046
|
callback(null);
|
|
1970
2047
|
}
|
|
1971
2048
|
} else {
|
|
1972
|
-
|
|
2049
|
+
warn('Document metadata fetch failed. Status:', xhr.status, 'Response:', xhr.responseText);
|
|
1973
2050
|
callback(null);
|
|
1974
2051
|
}
|
|
1975
2052
|
}
|
|
@@ -2353,6 +2430,14 @@ function generateLrsBridgeCode(options) {
|
|
|
2353
2430
|
function buildCourseActivityObject() {
|
|
2354
2431
|
if (!LRS.courseInfo) return null;
|
|
2355
2432
|
|
|
2433
|
+
// Warn if we don't have the required GUIDs for Bravais aggregation
|
|
2434
|
+
if (!LRS.courseInfo.guid) {
|
|
2435
|
+
warn('Missing document GUID - statement may fail Bravais aggregation. Using numeric ID:', LRS.courseInfo.documentId);
|
|
2436
|
+
}
|
|
2437
|
+
if (!LRS.courseInfo.versionGuid) {
|
|
2438
|
+
warn('Missing version GUID - statement may fail Bravais aggregation (document_version_id will be null)');
|
|
2439
|
+
}
|
|
2440
|
+
|
|
2356
2441
|
var obj = {
|
|
2357
2442
|
objectType: 'Activity',
|
|
2358
2443
|
id: LRS.courseInfo.id,
|
|
@@ -2365,10 +2450,13 @@ function generateLrsBridgeCode(options) {
|
|
|
2365
2450
|
// Add Xyleme-format extensions
|
|
2366
2451
|
obj.definition.extensions = {};
|
|
2367
2452
|
|
|
2368
|
-
// Version ID in Xyleme IRI format
|
|
2453
|
+
// Version ID in Xyleme IRI format - REQUIRED for Bravais aggregation
|
|
2369
2454
|
if (LRS.courseInfo.versionGuid) {
|
|
2370
2455
|
obj.definition.extensions['versionId'] =
|
|
2371
2456
|
'http://xyleme.com/bravais/document.version/' + LRS.courseInfo.versionGuid;
|
|
2457
|
+
} else {
|
|
2458
|
+
// Log error - this will cause aggregation failure
|
|
2459
|
+
warn('versionId extension not set - this statement will fail Bravais aggregation!');
|
|
2372
2460
|
}
|
|
2373
2461
|
|
|
2374
2462
|
// Format (e.g., "SCORM by Rise")
|
|
@@ -2974,49 +3062,146 @@ function generateLrsBridgeCode(options) {
|
|
|
2974
3062
|
function setupMediaInterceptors() {
|
|
2975
3063
|
if (!TRACK_MEDIA) return;
|
|
2976
3064
|
|
|
2977
|
-
|
|
2978
|
-
|
|
2979
|
-
|
|
3065
|
+
// Track which elements we've already attached listeners to
|
|
3066
|
+
var trackedMedia = new WeakSet();
|
|
3067
|
+
|
|
3068
|
+
// Helper to get media name/title from element or surrounding context
|
|
3069
|
+
function getMediaName(el) {
|
|
3070
|
+
// Check element attributes
|
|
3071
|
+
var name = el.title || el.getAttribute('aria-label') || el.getAttribute('data-name');
|
|
3072
|
+
if (name) return name;
|
|
3073
|
+
|
|
3074
|
+
// Check parent Rise video block for title
|
|
3075
|
+
var videoBlock = findClosest(el, '[data-block-type="video"], .blocks-video, .video-block');
|
|
3076
|
+
if (videoBlock) {
|
|
3077
|
+
var titleEl = videoBlock.querySelector('.video-title, .block-title, [class*="title"]');
|
|
3078
|
+
if (titleEl) return titleEl.textContent.trim();
|
|
3079
|
+
}
|
|
3080
|
+
|
|
3081
|
+
// Use src filename as fallback
|
|
3082
|
+
var src = el.src || el.currentSrc || '';
|
|
3083
|
+
if (src) {
|
|
3084
|
+
var filename = src.split('/').pop().split('?')[0];
|
|
3085
|
+
if (filename && filename.length < 100) return filename;
|
|
3086
|
+
}
|
|
3087
|
+
|
|
3088
|
+
return el.tagName.toLowerCase();
|
|
3089
|
+
}
|
|
3090
|
+
|
|
3091
|
+
// Attach media event listeners to a single element
|
|
3092
|
+
function attachMediaListeners(el) {
|
|
3093
|
+
if (trackedMedia.has(el)) return;
|
|
3094
|
+
trackedMedia.add(el);
|
|
3095
|
+
|
|
3096
|
+
var mediaType = el.tagName.toLowerCase();
|
|
3097
|
+
log('Attaching media listeners to:', mediaType, el.src || el.currentSrc || 'no-src');
|
|
3098
|
+
|
|
3099
|
+
el.addEventListener('play', function() {
|
|
3100
|
+
log('Media play event captured:', mediaType);
|
|
2980
3101
|
LRS.mediaPlayed({
|
|
2981
|
-
type:
|
|
2982
|
-
src:
|
|
2983
|
-
name:
|
|
2984
|
-
currentTime:
|
|
2985
|
-
duration:
|
|
3102
|
+
type: mediaType,
|
|
3103
|
+
src: el.src || el.currentSrc || '',
|
|
3104
|
+
name: getMediaName(el),
|
|
3105
|
+
currentTime: el.currentTime || 0,
|
|
3106
|
+
duration: el.duration || 0,
|
|
2986
3107
|
action: 'play'
|
|
2987
3108
|
});
|
|
2988
|
-
}
|
|
2989
|
-
}, true);
|
|
3109
|
+
});
|
|
2990
3110
|
|
|
2991
|
-
|
|
2992
|
-
|
|
2993
|
-
|
|
3111
|
+
el.addEventListener('pause', function() {
|
|
3112
|
+
// Ignore pause events that fire right before ended
|
|
3113
|
+
if (el.ended) return;
|
|
3114
|
+
log('Media pause event captured:', mediaType);
|
|
2994
3115
|
LRS.mediaPlayed({
|
|
2995
|
-
type:
|
|
2996
|
-
src:
|
|
2997
|
-
name:
|
|
2998
|
-
currentTime:
|
|
2999
|
-
duration:
|
|
3116
|
+
type: mediaType,
|
|
3117
|
+
src: el.src || el.currentSrc || '',
|
|
3118
|
+
name: getMediaName(el),
|
|
3119
|
+
currentTime: el.currentTime || 0,
|
|
3120
|
+
duration: el.duration || 0,
|
|
3000
3121
|
action: 'pause'
|
|
3001
3122
|
});
|
|
3002
|
-
}
|
|
3003
|
-
}, true);
|
|
3123
|
+
});
|
|
3004
3124
|
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
if (target.tagName === 'VIDEO' || target.tagName === 'AUDIO') {
|
|
3125
|
+
el.addEventListener('ended', function() {
|
|
3126
|
+
log('Media ended event captured:', mediaType);
|
|
3008
3127
|
LRS.mediaPlayed({
|
|
3009
|
-
type:
|
|
3010
|
-
src:
|
|
3011
|
-
name:
|
|
3012
|
-
currentTime:
|
|
3013
|
-
duration:
|
|
3128
|
+
type: mediaType,
|
|
3129
|
+
src: el.src || el.currentSrc || '',
|
|
3130
|
+
name: getMediaName(el),
|
|
3131
|
+
currentTime: el.duration || 0,
|
|
3132
|
+
duration: el.duration || 0,
|
|
3014
3133
|
action: 'completed'
|
|
3015
3134
|
});
|
|
3135
|
+
});
|
|
3136
|
+
}
|
|
3137
|
+
|
|
3138
|
+
// Scan for and attach listeners to all video/audio elements
|
|
3139
|
+
function scanForMedia(root) {
|
|
3140
|
+
var elements = (root || document).querySelectorAll('video, audio');
|
|
3141
|
+
log('Scanning for media elements, found:', elements.length);
|
|
3142
|
+
for (var i = 0; i < elements.length; i++) {
|
|
3143
|
+
attachMediaListeners(elements[i]);
|
|
3144
|
+
}
|
|
3145
|
+
}
|
|
3146
|
+
|
|
3147
|
+
// Initial scan
|
|
3148
|
+
scanForMedia();
|
|
3149
|
+
|
|
3150
|
+
// Document-level capture for any we might miss
|
|
3151
|
+
document.addEventListener('play', function(e) {
|
|
3152
|
+
var target = e.target;
|
|
3153
|
+
if (target.tagName === 'VIDEO' || target.tagName === 'AUDIO') {
|
|
3154
|
+
// Attach listeners if not already tracked
|
|
3155
|
+
if (!trackedMedia.has(target)) {
|
|
3156
|
+
log('Captured play from untracked media, attaching listeners');
|
|
3157
|
+
attachMediaListeners(target);
|
|
3158
|
+
}
|
|
3016
3159
|
}
|
|
3017
3160
|
}, true);
|
|
3018
3161
|
|
|
3019
|
-
|
|
3162
|
+
// MutationObserver to detect dynamically added video/audio elements
|
|
3163
|
+
var mediaObserver = new MutationObserver(function(mutations) {
|
|
3164
|
+
var needsScan = false;
|
|
3165
|
+
mutations.forEach(function(mutation) {
|
|
3166
|
+
mutation.addedNodes.forEach(function(node) {
|
|
3167
|
+
if (node.nodeType === 1) {
|
|
3168
|
+
if (node.tagName === 'VIDEO' || node.tagName === 'AUDIO') {
|
|
3169
|
+
attachMediaListeners(node);
|
|
3170
|
+
} else if (node.querySelectorAll) {
|
|
3171
|
+
// Check for nested video/audio
|
|
3172
|
+
var nested = node.querySelectorAll('video, audio');
|
|
3173
|
+
if (nested.length > 0) {
|
|
3174
|
+
needsScan = true;
|
|
3175
|
+
}
|
|
3176
|
+
}
|
|
3177
|
+
}
|
|
3178
|
+
});
|
|
3179
|
+
});
|
|
3180
|
+
if (needsScan) {
|
|
3181
|
+
scanForMedia();
|
|
3182
|
+
}
|
|
3183
|
+
});
|
|
3184
|
+
|
|
3185
|
+
// Start observing once DOM is ready
|
|
3186
|
+
function startMediaObserver() {
|
|
3187
|
+
if (document.body) {
|
|
3188
|
+
mediaObserver.observe(document.body, {
|
|
3189
|
+
childList: true,
|
|
3190
|
+
subtree: true
|
|
3191
|
+
});
|
|
3192
|
+
log('Media mutation observer started');
|
|
3193
|
+
} else {
|
|
3194
|
+
setTimeout(startMediaObserver, 100);
|
|
3195
|
+
}
|
|
3196
|
+
}
|
|
3197
|
+
startMediaObserver();
|
|
3198
|
+
|
|
3199
|
+
// Periodic rescan for Rise lazy-loaded content
|
|
3200
|
+
setInterval(function() {
|
|
3201
|
+
scanForMedia();
|
|
3202
|
+
}, 3000);
|
|
3203
|
+
|
|
3204
|
+
log('Media interceptors set up (enhanced)');
|
|
3020
3205
|
}
|
|
3021
3206
|
|
|
3022
3207
|
function setupNavigationInterceptors() {
|
|
@@ -3257,7 +3442,7 @@ function generateLrsBridgeCode(options) {
|
|
|
3257
3442
|
// ========================================================================
|
|
3258
3443
|
|
|
3259
3444
|
function init() {
|
|
3260
|
-
log('Initializing LRS bridge v2.
|
|
3445
|
+
log('Initializing LRS bridge v2.3.0...');
|
|
3261
3446
|
|
|
3262
3447
|
// Extract course info early
|
|
3263
3448
|
extractCourseInfo();
|
|
@@ -3293,9 +3478,18 @@ function generateLrsBridgeCode(options) {
|
|
|
3293
3478
|
|
|
3294
3479
|
// Fetch document metadata from API to get GUIDs (async)
|
|
3295
3480
|
// Then send course launched event
|
|
3481
|
+
var sharedLinkToken = LRS.courseInfo ? LRS.courseInfo.sharedLinkToken : null;
|
|
3296
3482
|
var documentId = LRS.courseInfo ? LRS.courseInfo.documentId : null;
|
|
3297
3483
|
|
|
3298
3484
|
function sendLaunchEvents() {
|
|
3485
|
+
// Log the course info state before sending statements
|
|
3486
|
+
log('Sending launch events with course info:', {
|
|
3487
|
+
id: LRS.courseInfo ? LRS.courseInfo.id : null,
|
|
3488
|
+
guid: LRS.courseInfo ? LRS.courseInfo.guid : null,
|
|
3489
|
+
versionGuid: LRS.courseInfo ? LRS.courseInfo.versionGuid : null,
|
|
3490
|
+
documentId: LRS.courseInfo ? LRS.courseInfo.documentId : null
|
|
3491
|
+
});
|
|
3492
|
+
|
|
3299
3493
|
if (TRACK_NAVIGATION) {
|
|
3300
3494
|
LRS.courseLaunched();
|
|
3301
3495
|
LRS.contentOpened({
|
|
@@ -3306,17 +3500,52 @@ function generateLrsBridgeCode(options) {
|
|
|
3306
3500
|
}
|
|
3307
3501
|
}
|
|
3308
3502
|
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
fetchDocumentMetadata(documentId, function(docData) {
|
|
3503
|
+
function fetchDocDataAndSend(docId) {
|
|
3504
|
+
fetchDocumentMetadata(docId, function(docData) {
|
|
3312
3505
|
if (docData) {
|
|
3313
3506
|
updateCourseInfoFromApi(docData);
|
|
3314
3507
|
}
|
|
3315
3508
|
// Send launch events after API fetch (success or failure)
|
|
3316
3509
|
setTimeout(sendLaunchEvents, 100);
|
|
3317
3510
|
});
|
|
3511
|
+
}
|
|
3512
|
+
|
|
3513
|
+
if (!LRS.courseInfo.guid) {
|
|
3514
|
+
// First, try to get real document ID from shared link token
|
|
3515
|
+
// The URL path contains a thin pack file ID, not the real document ID
|
|
3516
|
+
if (sharedLinkToken) {
|
|
3517
|
+
log('Fetching shared link data to get real document ID...');
|
|
3518
|
+
fetchSharedLinkData(sharedLinkToken, function(linkData) {
|
|
3519
|
+
if (linkData && linkData.documentId) {
|
|
3520
|
+
// Got the real document ID from shared link
|
|
3521
|
+
log('Got real document ID from shared link:', linkData.documentId);
|
|
3522
|
+
LRS.courseInfo.documentId = linkData.documentId;
|
|
3523
|
+
// Update shared link name if available
|
|
3524
|
+
if (linkData.documentName) {
|
|
3525
|
+
LRS.courseInfo.sharedLinkName = linkData.documentName;
|
|
3526
|
+
}
|
|
3527
|
+
fetchDocDataAndSend(linkData.documentId);
|
|
3528
|
+
} else if (documentId) {
|
|
3529
|
+
// Fallback to extracted document ID (may be thin pack ID)
|
|
3530
|
+
warn('Could not get document ID from shared link, using extracted ID:', documentId);
|
|
3531
|
+
fetchDocDataAndSend(documentId);
|
|
3532
|
+
} else {
|
|
3533
|
+
// No document ID available
|
|
3534
|
+
warn('No document ID available - statements may fail aggregation');
|
|
3535
|
+
setTimeout(sendLaunchEvents, 100);
|
|
3536
|
+
}
|
|
3537
|
+
});
|
|
3538
|
+
} else if (documentId) {
|
|
3539
|
+
// No shared link token, try with extracted document ID
|
|
3540
|
+
log('No shared link token, fetching document data with ID:', documentId);
|
|
3541
|
+
fetchDocDataAndSend(documentId);
|
|
3542
|
+
} else {
|
|
3543
|
+
// No identifiers available
|
|
3544
|
+
warn('No shared link token or document ID - statements may fail aggregation');
|
|
3545
|
+
setTimeout(sendLaunchEvents, 500);
|
|
3546
|
+
}
|
|
3318
3547
|
} else {
|
|
3319
|
-
// Already have GUID
|
|
3548
|
+
// Already have GUID - send after short delay
|
|
3320
3549
|
setTimeout(sendLaunchEvents, 500);
|
|
3321
3550
|
}
|
|
3322
3551
|
|