@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.cjs
CHANGED
|
@@ -1909,24 +1909,12 @@ function generateLrsBridgeCode(options) {
|
|
|
1909
1909
|
// Store for document API data
|
|
1910
1910
|
var documentApiData = null;
|
|
1911
1911
|
var documentApiFetched = false;
|
|
1912
|
+
var sharedLinkApiData = null;
|
|
1912
1913
|
|
|
1913
1914
|
/**
|
|
1914
|
-
*
|
|
1915
|
-
* Endpoint: /api/v3/documents/{documentId}
|
|
1915
|
+
* Detect Bravais core URL from current page context
|
|
1916
1916
|
*/
|
|
1917
|
-
function
|
|
1918
|
-
if (!documentId) {
|
|
1919
|
-
callback(null);
|
|
1920
|
-
return;
|
|
1921
|
-
}
|
|
1922
|
-
|
|
1923
|
-
if (documentApiFetched) {
|
|
1924
|
-
callback(documentApiData);
|
|
1925
|
-
return;
|
|
1926
|
-
}
|
|
1927
|
-
|
|
1928
|
-
// Detect Bravais core URL
|
|
1929
|
-
var coreUrl = null;
|
|
1917
|
+
function detectCoreUrl() {
|
|
1930
1918
|
var urlsToCheck = [window.location.href, document.referrer];
|
|
1931
1919
|
|
|
1932
1920
|
try {
|
|
@@ -1942,13 +1930,91 @@ function generateLrsBridgeCode(options) {
|
|
|
1942
1930
|
} catch (e) {}
|
|
1943
1931
|
|
|
1944
1932
|
for (var j = 0; j < urlsToCheck.length; j++) {
|
|
1945
|
-
var match = urlsToCheck[j].match(/(https
|
|
1933
|
+
var match = urlsToCheck[j].match(/(https?://core-[a-zA-Z0-9_-]+.bravais.com)/);
|
|
1946
1934
|
if (match) {
|
|
1947
|
-
|
|
1948
|
-
break;
|
|
1935
|
+
return match[1];
|
|
1949
1936
|
}
|
|
1950
1937
|
}
|
|
1938
|
+
return null;
|
|
1939
|
+
}
|
|
1940
|
+
|
|
1941
|
+
/**
|
|
1942
|
+
* Fetch shared link data from Bravais API to get the real document ID
|
|
1943
|
+
* Endpoint: /api/v3/sharedLinks/token/{token}
|
|
1944
|
+
* Returns: { documentId, documentName, format, ... }
|
|
1945
|
+
*/
|
|
1946
|
+
function fetchSharedLinkData(token, callback) {
|
|
1947
|
+
if (!token) {
|
|
1948
|
+
callback(null);
|
|
1949
|
+
return;
|
|
1950
|
+
}
|
|
1951
1951
|
|
|
1952
|
+
var coreUrl = detectCoreUrl();
|
|
1953
|
+
if (!coreUrl) {
|
|
1954
|
+
log('No Bravais core URL detected for shared link fetch');
|
|
1955
|
+
callback(null);
|
|
1956
|
+
return;
|
|
1957
|
+
}
|
|
1958
|
+
|
|
1959
|
+
var apiUrl = coreUrl + '/api/v3/sharedLinks/token/' + token;
|
|
1960
|
+
log('Fetching shared link data from:', apiUrl);
|
|
1961
|
+
|
|
1962
|
+
var xhr = new XMLHttpRequest();
|
|
1963
|
+
xhr.open('GET', apiUrl, true);
|
|
1964
|
+
xhr.withCredentials = true;
|
|
1965
|
+
xhr.setRequestHeader('Accept', 'application/json');
|
|
1966
|
+
|
|
1967
|
+
xhr.onreadystatechange = function() {
|
|
1968
|
+
if (xhr.readyState === 4) {
|
|
1969
|
+
log('Shared link API response status:', xhr.status);
|
|
1970
|
+
if (xhr.status >= 200 && xhr.status < 300) {
|
|
1971
|
+
try {
|
|
1972
|
+
sharedLinkApiData = JSON.parse(xhr.responseText);
|
|
1973
|
+
log('Shared link data received:', sharedLinkApiData);
|
|
1974
|
+
if (sharedLinkApiData.documentId) {
|
|
1975
|
+
log('Real document ID from shared link:', sharedLinkApiData.documentId);
|
|
1976
|
+
}
|
|
1977
|
+
callback(sharedLinkApiData);
|
|
1978
|
+
} catch (e) {
|
|
1979
|
+
warn('Error parsing shared link data:', e);
|
|
1980
|
+
callback(null);
|
|
1981
|
+
}
|
|
1982
|
+
} else {
|
|
1983
|
+
warn('Shared link fetch failed. Status:', xhr.status);
|
|
1984
|
+
callback(null);
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
};
|
|
1988
|
+
|
|
1989
|
+
xhr.onerror = function() {
|
|
1990
|
+
log('Network error fetching shared link data');
|
|
1991
|
+
callback(null);
|
|
1992
|
+
};
|
|
1993
|
+
|
|
1994
|
+
try {
|
|
1995
|
+
xhr.send();
|
|
1996
|
+
} catch (e) {
|
|
1997
|
+
warn('Error sending shared link request:', e);
|
|
1998
|
+
callback(null);
|
|
1999
|
+
}
|
|
2000
|
+
}
|
|
2001
|
+
|
|
2002
|
+
/**
|
|
2003
|
+
* Fetch document metadata from Bravais API to get GUIDs
|
|
2004
|
+
* Endpoint: /api/v3/documents/{documentId}
|
|
2005
|
+
*/
|
|
2006
|
+
function fetchDocumentMetadata(documentId, callback) {
|
|
2007
|
+
if (!documentId) {
|
|
2008
|
+
callback(null);
|
|
2009
|
+
return;
|
|
2010
|
+
}
|
|
2011
|
+
|
|
2012
|
+
if (documentApiFetched) {
|
|
2013
|
+
callback(documentApiData);
|
|
2014
|
+
return;
|
|
2015
|
+
}
|
|
2016
|
+
|
|
2017
|
+
var coreUrl = detectCoreUrl();
|
|
1952
2018
|
if (!coreUrl) {
|
|
1953
2019
|
log('No Bravais core URL detected for document metadata fetch');
|
|
1954
2020
|
documentApiFetched = true;
|
|
@@ -1972,13 +2038,24 @@ function generateLrsBridgeCode(options) {
|
|
|
1972
2038
|
try {
|
|
1973
2039
|
documentApiData = JSON.parse(xhr.responseText);
|
|
1974
2040
|
log('Document metadata received:', documentApiData);
|
|
2041
|
+
// Validate that we got the required fields
|
|
2042
|
+
if (documentApiData.guid) {
|
|
2043
|
+
log('Document GUID found:', documentApiData.guid);
|
|
2044
|
+
} else {
|
|
2045
|
+
warn('Document API response missing guid field!', documentApiData);
|
|
2046
|
+
}
|
|
2047
|
+
if (documentApiData.latestVersion && documentApiData.latestVersion.guid) {
|
|
2048
|
+
log('Version GUID found:', documentApiData.latestVersion.guid);
|
|
2049
|
+
} else {
|
|
2050
|
+
warn('Document API response missing latestVersion.guid field!', documentApiData);
|
|
2051
|
+
}
|
|
1975
2052
|
callback(documentApiData);
|
|
1976
2053
|
} catch (e) {
|
|
1977
2054
|
warn('Error parsing document metadata:', e);
|
|
1978
2055
|
callback(null);
|
|
1979
2056
|
}
|
|
1980
2057
|
} else {
|
|
1981
|
-
|
|
2058
|
+
warn('Document metadata fetch failed. Status:', xhr.status, 'Response:', xhr.responseText);
|
|
1982
2059
|
callback(null);
|
|
1983
2060
|
}
|
|
1984
2061
|
}
|
|
@@ -2362,6 +2439,14 @@ function generateLrsBridgeCode(options) {
|
|
|
2362
2439
|
function buildCourseActivityObject() {
|
|
2363
2440
|
if (!LRS.courseInfo) return null;
|
|
2364
2441
|
|
|
2442
|
+
// Warn if we don't have the required GUIDs for Bravais aggregation
|
|
2443
|
+
if (!LRS.courseInfo.guid) {
|
|
2444
|
+
warn('Missing document GUID - statement may fail Bravais aggregation. Using numeric ID:', LRS.courseInfo.documentId);
|
|
2445
|
+
}
|
|
2446
|
+
if (!LRS.courseInfo.versionGuid) {
|
|
2447
|
+
warn('Missing version GUID - statement may fail Bravais aggregation (document_version_id will be null)');
|
|
2448
|
+
}
|
|
2449
|
+
|
|
2365
2450
|
var obj = {
|
|
2366
2451
|
objectType: 'Activity',
|
|
2367
2452
|
id: LRS.courseInfo.id,
|
|
@@ -2374,10 +2459,13 @@ function generateLrsBridgeCode(options) {
|
|
|
2374
2459
|
// Add Xyleme-format extensions
|
|
2375
2460
|
obj.definition.extensions = {};
|
|
2376
2461
|
|
|
2377
|
-
// Version ID in Xyleme IRI format
|
|
2462
|
+
// Version ID in Xyleme IRI format - REQUIRED for Bravais aggregation
|
|
2378
2463
|
if (LRS.courseInfo.versionGuid) {
|
|
2379
2464
|
obj.definition.extensions['versionId'] =
|
|
2380
2465
|
'http://xyleme.com/bravais/document.version/' + LRS.courseInfo.versionGuid;
|
|
2466
|
+
} else {
|
|
2467
|
+
// Log error - this will cause aggregation failure
|
|
2468
|
+
warn('versionId extension not set - this statement will fail Bravais aggregation!');
|
|
2381
2469
|
}
|
|
2382
2470
|
|
|
2383
2471
|
// Format (e.g., "SCORM by Rise")
|
|
@@ -2983,49 +3071,146 @@ function generateLrsBridgeCode(options) {
|
|
|
2983
3071
|
function setupMediaInterceptors() {
|
|
2984
3072
|
if (!TRACK_MEDIA) return;
|
|
2985
3073
|
|
|
2986
|
-
|
|
2987
|
-
|
|
2988
|
-
|
|
3074
|
+
// Track which elements we've already attached listeners to
|
|
3075
|
+
var trackedMedia = new WeakSet();
|
|
3076
|
+
|
|
3077
|
+
// Helper to get media name/title from element or surrounding context
|
|
3078
|
+
function getMediaName(el) {
|
|
3079
|
+
// Check element attributes
|
|
3080
|
+
var name = el.title || el.getAttribute('aria-label') || el.getAttribute('data-name');
|
|
3081
|
+
if (name) return name;
|
|
3082
|
+
|
|
3083
|
+
// Check parent Rise video block for title
|
|
3084
|
+
var videoBlock = findClosest(el, '[data-block-type="video"], .blocks-video, .video-block');
|
|
3085
|
+
if (videoBlock) {
|
|
3086
|
+
var titleEl = videoBlock.querySelector('.video-title, .block-title, [class*="title"]');
|
|
3087
|
+
if (titleEl) return titleEl.textContent.trim();
|
|
3088
|
+
}
|
|
3089
|
+
|
|
3090
|
+
// Use src filename as fallback
|
|
3091
|
+
var src = el.src || el.currentSrc || '';
|
|
3092
|
+
if (src) {
|
|
3093
|
+
var filename = src.split('/').pop().split('?')[0];
|
|
3094
|
+
if (filename && filename.length < 100) return filename;
|
|
3095
|
+
}
|
|
3096
|
+
|
|
3097
|
+
return el.tagName.toLowerCase();
|
|
3098
|
+
}
|
|
3099
|
+
|
|
3100
|
+
// Attach media event listeners to a single element
|
|
3101
|
+
function attachMediaListeners(el) {
|
|
3102
|
+
if (trackedMedia.has(el)) return;
|
|
3103
|
+
trackedMedia.add(el);
|
|
3104
|
+
|
|
3105
|
+
var mediaType = el.tagName.toLowerCase();
|
|
3106
|
+
log('Attaching media listeners to:', mediaType, el.src || el.currentSrc || 'no-src');
|
|
3107
|
+
|
|
3108
|
+
el.addEventListener('play', function() {
|
|
3109
|
+
log('Media play event captured:', mediaType);
|
|
2989
3110
|
LRS.mediaPlayed({
|
|
2990
|
-
type:
|
|
2991
|
-
src:
|
|
2992
|
-
name:
|
|
2993
|
-
currentTime:
|
|
2994
|
-
duration:
|
|
3111
|
+
type: mediaType,
|
|
3112
|
+
src: el.src || el.currentSrc || '',
|
|
3113
|
+
name: getMediaName(el),
|
|
3114
|
+
currentTime: el.currentTime || 0,
|
|
3115
|
+
duration: el.duration || 0,
|
|
2995
3116
|
action: 'play'
|
|
2996
3117
|
});
|
|
2997
|
-
}
|
|
2998
|
-
}, true);
|
|
3118
|
+
});
|
|
2999
3119
|
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3120
|
+
el.addEventListener('pause', function() {
|
|
3121
|
+
// Ignore pause events that fire right before ended
|
|
3122
|
+
if (el.ended) return;
|
|
3123
|
+
log('Media pause event captured:', mediaType);
|
|
3003
3124
|
LRS.mediaPlayed({
|
|
3004
|
-
type:
|
|
3005
|
-
src:
|
|
3006
|
-
name:
|
|
3007
|
-
currentTime:
|
|
3008
|
-
duration:
|
|
3125
|
+
type: mediaType,
|
|
3126
|
+
src: el.src || el.currentSrc || '',
|
|
3127
|
+
name: getMediaName(el),
|
|
3128
|
+
currentTime: el.currentTime || 0,
|
|
3129
|
+
duration: el.duration || 0,
|
|
3009
3130
|
action: 'pause'
|
|
3010
3131
|
});
|
|
3011
|
-
}
|
|
3012
|
-
}, true);
|
|
3132
|
+
});
|
|
3013
3133
|
|
|
3014
|
-
|
|
3015
|
-
|
|
3016
|
-
if (target.tagName === 'VIDEO' || target.tagName === 'AUDIO') {
|
|
3134
|
+
el.addEventListener('ended', function() {
|
|
3135
|
+
log('Media ended event captured:', mediaType);
|
|
3017
3136
|
LRS.mediaPlayed({
|
|
3018
|
-
type:
|
|
3019
|
-
src:
|
|
3020
|
-
name:
|
|
3021
|
-
currentTime:
|
|
3022
|
-
duration:
|
|
3137
|
+
type: mediaType,
|
|
3138
|
+
src: el.src || el.currentSrc || '',
|
|
3139
|
+
name: getMediaName(el),
|
|
3140
|
+
currentTime: el.duration || 0,
|
|
3141
|
+
duration: el.duration || 0,
|
|
3023
3142
|
action: 'completed'
|
|
3024
3143
|
});
|
|
3144
|
+
});
|
|
3145
|
+
}
|
|
3146
|
+
|
|
3147
|
+
// Scan for and attach listeners to all video/audio elements
|
|
3148
|
+
function scanForMedia(root) {
|
|
3149
|
+
var elements = (root || document).querySelectorAll('video, audio');
|
|
3150
|
+
log('Scanning for media elements, found:', elements.length);
|
|
3151
|
+
for (var i = 0; i < elements.length; i++) {
|
|
3152
|
+
attachMediaListeners(elements[i]);
|
|
3153
|
+
}
|
|
3154
|
+
}
|
|
3155
|
+
|
|
3156
|
+
// Initial scan
|
|
3157
|
+
scanForMedia();
|
|
3158
|
+
|
|
3159
|
+
// Document-level capture for any we might miss
|
|
3160
|
+
document.addEventListener('play', function(e) {
|
|
3161
|
+
var target = e.target;
|
|
3162
|
+
if (target.tagName === 'VIDEO' || target.tagName === 'AUDIO') {
|
|
3163
|
+
// Attach listeners if not already tracked
|
|
3164
|
+
if (!trackedMedia.has(target)) {
|
|
3165
|
+
log('Captured play from untracked media, attaching listeners');
|
|
3166
|
+
attachMediaListeners(target);
|
|
3167
|
+
}
|
|
3025
3168
|
}
|
|
3026
3169
|
}, true);
|
|
3027
3170
|
|
|
3028
|
-
|
|
3171
|
+
// MutationObserver to detect dynamically added video/audio elements
|
|
3172
|
+
var mediaObserver = new MutationObserver(function(mutations) {
|
|
3173
|
+
var needsScan = false;
|
|
3174
|
+
mutations.forEach(function(mutation) {
|
|
3175
|
+
mutation.addedNodes.forEach(function(node) {
|
|
3176
|
+
if (node.nodeType === 1) {
|
|
3177
|
+
if (node.tagName === 'VIDEO' || node.tagName === 'AUDIO') {
|
|
3178
|
+
attachMediaListeners(node);
|
|
3179
|
+
} else if (node.querySelectorAll) {
|
|
3180
|
+
// Check for nested video/audio
|
|
3181
|
+
var nested = node.querySelectorAll('video, audio');
|
|
3182
|
+
if (nested.length > 0) {
|
|
3183
|
+
needsScan = true;
|
|
3184
|
+
}
|
|
3185
|
+
}
|
|
3186
|
+
}
|
|
3187
|
+
});
|
|
3188
|
+
});
|
|
3189
|
+
if (needsScan) {
|
|
3190
|
+
scanForMedia();
|
|
3191
|
+
}
|
|
3192
|
+
});
|
|
3193
|
+
|
|
3194
|
+
// Start observing once DOM is ready
|
|
3195
|
+
function startMediaObserver() {
|
|
3196
|
+
if (document.body) {
|
|
3197
|
+
mediaObserver.observe(document.body, {
|
|
3198
|
+
childList: true,
|
|
3199
|
+
subtree: true
|
|
3200
|
+
});
|
|
3201
|
+
log('Media mutation observer started');
|
|
3202
|
+
} else {
|
|
3203
|
+
setTimeout(startMediaObserver, 100);
|
|
3204
|
+
}
|
|
3205
|
+
}
|
|
3206
|
+
startMediaObserver();
|
|
3207
|
+
|
|
3208
|
+
// Periodic rescan for Rise lazy-loaded content
|
|
3209
|
+
setInterval(function() {
|
|
3210
|
+
scanForMedia();
|
|
3211
|
+
}, 3000);
|
|
3212
|
+
|
|
3213
|
+
log('Media interceptors set up (enhanced)');
|
|
3029
3214
|
}
|
|
3030
3215
|
|
|
3031
3216
|
function setupNavigationInterceptors() {
|
|
@@ -3266,7 +3451,7 @@ function generateLrsBridgeCode(options) {
|
|
|
3266
3451
|
// ========================================================================
|
|
3267
3452
|
|
|
3268
3453
|
function init() {
|
|
3269
|
-
log('Initializing LRS bridge v2.
|
|
3454
|
+
log('Initializing LRS bridge v2.3.0...');
|
|
3270
3455
|
|
|
3271
3456
|
// Extract course info early
|
|
3272
3457
|
extractCourseInfo();
|
|
@@ -3302,9 +3487,18 @@ function generateLrsBridgeCode(options) {
|
|
|
3302
3487
|
|
|
3303
3488
|
// Fetch document metadata from API to get GUIDs (async)
|
|
3304
3489
|
// Then send course launched event
|
|
3490
|
+
var sharedLinkToken = LRS.courseInfo ? LRS.courseInfo.sharedLinkToken : null;
|
|
3305
3491
|
var documentId = LRS.courseInfo ? LRS.courseInfo.documentId : null;
|
|
3306
3492
|
|
|
3307
3493
|
function sendLaunchEvents() {
|
|
3494
|
+
// Log the course info state before sending statements
|
|
3495
|
+
log('Sending launch events with course info:', {
|
|
3496
|
+
id: LRS.courseInfo ? LRS.courseInfo.id : null,
|
|
3497
|
+
guid: LRS.courseInfo ? LRS.courseInfo.guid : null,
|
|
3498
|
+
versionGuid: LRS.courseInfo ? LRS.courseInfo.versionGuid : null,
|
|
3499
|
+
documentId: LRS.courseInfo ? LRS.courseInfo.documentId : null
|
|
3500
|
+
});
|
|
3501
|
+
|
|
3308
3502
|
if (TRACK_NAVIGATION) {
|
|
3309
3503
|
LRS.courseLaunched();
|
|
3310
3504
|
LRS.contentOpened({
|
|
@@ -3315,17 +3509,52 @@ function generateLrsBridgeCode(options) {
|
|
|
3315
3509
|
}
|
|
3316
3510
|
}
|
|
3317
3511
|
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
fetchDocumentMetadata(documentId, function(docData) {
|
|
3512
|
+
function fetchDocDataAndSend(docId) {
|
|
3513
|
+
fetchDocumentMetadata(docId, function(docData) {
|
|
3321
3514
|
if (docData) {
|
|
3322
3515
|
updateCourseInfoFromApi(docData);
|
|
3323
3516
|
}
|
|
3324
3517
|
// Send launch events after API fetch (success or failure)
|
|
3325
3518
|
setTimeout(sendLaunchEvents, 100);
|
|
3326
3519
|
});
|
|
3520
|
+
}
|
|
3521
|
+
|
|
3522
|
+
if (!LRS.courseInfo.guid) {
|
|
3523
|
+
// First, try to get real document ID from shared link token
|
|
3524
|
+
// The URL path contains a thin pack file ID, not the real document ID
|
|
3525
|
+
if (sharedLinkToken) {
|
|
3526
|
+
log('Fetching shared link data to get real document ID...');
|
|
3527
|
+
fetchSharedLinkData(sharedLinkToken, function(linkData) {
|
|
3528
|
+
if (linkData && linkData.documentId) {
|
|
3529
|
+
// Got the real document ID from shared link
|
|
3530
|
+
log('Got real document ID from shared link:', linkData.documentId);
|
|
3531
|
+
LRS.courseInfo.documentId = linkData.documentId;
|
|
3532
|
+
// Update shared link name if available
|
|
3533
|
+
if (linkData.documentName) {
|
|
3534
|
+
LRS.courseInfo.sharedLinkName = linkData.documentName;
|
|
3535
|
+
}
|
|
3536
|
+
fetchDocDataAndSend(linkData.documentId);
|
|
3537
|
+
} else if (documentId) {
|
|
3538
|
+
// Fallback to extracted document ID (may be thin pack ID)
|
|
3539
|
+
warn('Could not get document ID from shared link, using extracted ID:', documentId);
|
|
3540
|
+
fetchDocDataAndSend(documentId);
|
|
3541
|
+
} else {
|
|
3542
|
+
// No document ID available
|
|
3543
|
+
warn('No document ID available - statements may fail aggregation');
|
|
3544
|
+
setTimeout(sendLaunchEvents, 100);
|
|
3545
|
+
}
|
|
3546
|
+
});
|
|
3547
|
+
} else if (documentId) {
|
|
3548
|
+
// No shared link token, try with extracted document ID
|
|
3549
|
+
log('No shared link token, fetching document data with ID:', documentId);
|
|
3550
|
+
fetchDocDataAndSend(documentId);
|
|
3551
|
+
} else {
|
|
3552
|
+
// No identifiers available
|
|
3553
|
+
warn('No shared link token or document ID - statements may fail aggregation');
|
|
3554
|
+
setTimeout(sendLaunchEvents, 500);
|
|
3555
|
+
}
|
|
3327
3556
|
} else {
|
|
3328
|
-
// Already have GUID
|
|
3557
|
+
// Already have GUID - send after short delay
|
|
3329
3558
|
setTimeout(sendLaunchEvents, 500);
|
|
3330
3559
|
}
|
|
3331
3560
|
|