@patch-adams/core 1.4.36 → 1.5.0
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 +313 -13
- package/dist/cli.cjs.map +1 -1
- package/dist/cli.js +313 -13
- package/dist/cli.js.map +1 -1
- package/dist/index.cjs +313 -13
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +27 -1
- package/dist/index.d.ts +27 -1
- package/dist/index.js +313 -13
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -291,6 +291,8 @@ declare const PatchAdamsConfigSchema: z.ZodObject<{
|
|
|
291
291
|
/** Whether this plugin is enabled */
|
|
292
292
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
293
293
|
}, z.ZodTypeAny, "passthrough">>>>;
|
|
294
|
+
/** Optional skin name — adds 'pa-skinned' + skin class to <html>, loads skin CSS/JS after core files */
|
|
295
|
+
skin: z.ZodOptional<z.ZodString>;
|
|
294
296
|
}, "strip", z.ZodTypeAny, {
|
|
295
297
|
remoteDomain: string;
|
|
296
298
|
htmlClass: string;
|
|
@@ -341,6 +343,7 @@ declare const PatchAdamsConfigSchema: z.ZodObject<{
|
|
|
341
343
|
/** Whether this plugin is enabled */
|
|
342
344
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
343
345
|
}, z.ZodTypeAny, "passthrough">>;
|
|
346
|
+
skin?: string | undefined;
|
|
344
347
|
}, {
|
|
345
348
|
remoteDomain: string;
|
|
346
349
|
htmlClass?: string | undefined;
|
|
@@ -391,6 +394,7 @@ declare const PatchAdamsConfigSchema: z.ZodObject<{
|
|
|
391
394
|
/** Whether this plugin is enabled */
|
|
392
395
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
393
396
|
}, z.ZodTypeAny, "passthrough">> | undefined;
|
|
397
|
+
skin?: string | undefined;
|
|
394
398
|
}>;
|
|
395
399
|
type PatchAdamsConfig = z.infer<typeof PatchAdamsConfigSchema>;
|
|
396
400
|
type LrsBridgeConfig = z.infer<typeof LrsBridgeConfigSchema>;
|
|
@@ -698,6 +702,14 @@ declare class HtmlInjector {
|
|
|
698
702
|
* Inject JS After loader at end of <body> (after __loadEntry)
|
|
699
703
|
*/
|
|
700
704
|
private injectJsAfter;
|
|
705
|
+
/**
|
|
706
|
+
* Inject Skin CSS loader after CSS After (before </head>)
|
|
707
|
+
*/
|
|
708
|
+
private injectSkinCss;
|
|
709
|
+
/**
|
|
710
|
+
* Inject Skin JS loader after JS After (before </body>)
|
|
711
|
+
*/
|
|
712
|
+
private injectSkinJs;
|
|
701
713
|
}
|
|
702
714
|
|
|
703
715
|
/**
|
|
@@ -770,6 +782,14 @@ declare class StorylineHtmlInjector {
|
|
|
770
782
|
* We inject before </body> so our code runs before the bootstrapper.
|
|
771
783
|
*/
|
|
772
784
|
private injectJsAfter;
|
|
785
|
+
/**
|
|
786
|
+
* Inject Skin CSS loader after CSS After (before </head>)
|
|
787
|
+
*/
|
|
788
|
+
private injectSkinCss;
|
|
789
|
+
/**
|
|
790
|
+
* Inject Skin JS loader after JS After (before </body>)
|
|
791
|
+
*/
|
|
792
|
+
private injectSkinJs;
|
|
773
793
|
}
|
|
774
794
|
|
|
775
795
|
interface ManifestPaths {
|
|
@@ -777,6 +797,8 @@ interface ManifestPaths {
|
|
|
777
797
|
cssAfter?: string;
|
|
778
798
|
jsBefore?: string;
|
|
779
799
|
jsAfter?: string;
|
|
800
|
+
skinCss?: string;
|
|
801
|
+
skinJs?: string;
|
|
780
802
|
}
|
|
781
803
|
/**
|
|
782
804
|
* Update manifest files to include injected assets
|
|
@@ -840,6 +862,8 @@ interface PatchOptions {
|
|
|
840
862
|
jsAfterContent?: string;
|
|
841
863
|
/** Skip fetching remote files even if fetchFallbacks is enabled in config */
|
|
842
864
|
skipFetch?: boolean;
|
|
865
|
+
/** Optional skin name — per-call override for config.skin */
|
|
866
|
+
skin?: string;
|
|
843
867
|
}
|
|
844
868
|
/**
|
|
845
869
|
* Main Patcher class for patching e-learning course packages
|
|
@@ -912,7 +936,7 @@ declare class Patcher {
|
|
|
912
936
|
* @param buffer - Input ZIP file as Buffer
|
|
913
937
|
* @returns Patched ZIP file as Buffer
|
|
914
938
|
*/
|
|
915
|
-
patchBuffer(buffer: Buffer): Promise<Buffer>;
|
|
939
|
+
patchBuffer(buffer: Buffer, options?: PatchOptions): Promise<Buffer>;
|
|
916
940
|
}
|
|
917
941
|
|
|
918
942
|
/**
|
|
@@ -1030,6 +1054,8 @@ interface JsBeforeOptions {
|
|
|
1030
1054
|
metadata: CourseMetadata | null;
|
|
1031
1055
|
/** LRS Bridge configuration */
|
|
1032
1056
|
lrsBridge: LrsBridgeOptions;
|
|
1057
|
+
/** Optional skin name */
|
|
1058
|
+
skin?: string;
|
|
1033
1059
|
}
|
|
1034
1060
|
/**
|
|
1035
1061
|
* Generate the blocking JS loader for the "before" slot
|
package/dist/index.d.ts
CHANGED
|
@@ -291,6 +291,8 @@ declare const PatchAdamsConfigSchema: z.ZodObject<{
|
|
|
291
291
|
/** Whether this plugin is enabled */
|
|
292
292
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
293
293
|
}, z.ZodTypeAny, "passthrough">>>>;
|
|
294
|
+
/** Optional skin name — adds 'pa-skinned' + skin class to <html>, loads skin CSS/JS after core files */
|
|
295
|
+
skin: z.ZodOptional<z.ZodString>;
|
|
294
296
|
}, "strip", z.ZodTypeAny, {
|
|
295
297
|
remoteDomain: string;
|
|
296
298
|
htmlClass: string;
|
|
@@ -341,6 +343,7 @@ declare const PatchAdamsConfigSchema: z.ZodObject<{
|
|
|
341
343
|
/** Whether this plugin is enabled */
|
|
342
344
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
343
345
|
}, z.ZodTypeAny, "passthrough">>;
|
|
346
|
+
skin?: string | undefined;
|
|
344
347
|
}, {
|
|
345
348
|
remoteDomain: string;
|
|
346
349
|
htmlClass?: string | undefined;
|
|
@@ -391,6 +394,7 @@ declare const PatchAdamsConfigSchema: z.ZodObject<{
|
|
|
391
394
|
/** Whether this plugin is enabled */
|
|
392
395
|
enabled: z.ZodDefault<z.ZodBoolean>;
|
|
393
396
|
}, z.ZodTypeAny, "passthrough">> | undefined;
|
|
397
|
+
skin?: string | undefined;
|
|
394
398
|
}>;
|
|
395
399
|
type PatchAdamsConfig = z.infer<typeof PatchAdamsConfigSchema>;
|
|
396
400
|
type LrsBridgeConfig = z.infer<typeof LrsBridgeConfigSchema>;
|
|
@@ -698,6 +702,14 @@ declare class HtmlInjector {
|
|
|
698
702
|
* Inject JS After loader at end of <body> (after __loadEntry)
|
|
699
703
|
*/
|
|
700
704
|
private injectJsAfter;
|
|
705
|
+
/**
|
|
706
|
+
* Inject Skin CSS loader after CSS After (before </head>)
|
|
707
|
+
*/
|
|
708
|
+
private injectSkinCss;
|
|
709
|
+
/**
|
|
710
|
+
* Inject Skin JS loader after JS After (before </body>)
|
|
711
|
+
*/
|
|
712
|
+
private injectSkinJs;
|
|
701
713
|
}
|
|
702
714
|
|
|
703
715
|
/**
|
|
@@ -770,6 +782,14 @@ declare class StorylineHtmlInjector {
|
|
|
770
782
|
* We inject before </body> so our code runs before the bootstrapper.
|
|
771
783
|
*/
|
|
772
784
|
private injectJsAfter;
|
|
785
|
+
/**
|
|
786
|
+
* Inject Skin CSS loader after CSS After (before </head>)
|
|
787
|
+
*/
|
|
788
|
+
private injectSkinCss;
|
|
789
|
+
/**
|
|
790
|
+
* Inject Skin JS loader after JS After (before </body>)
|
|
791
|
+
*/
|
|
792
|
+
private injectSkinJs;
|
|
773
793
|
}
|
|
774
794
|
|
|
775
795
|
interface ManifestPaths {
|
|
@@ -777,6 +797,8 @@ interface ManifestPaths {
|
|
|
777
797
|
cssAfter?: string;
|
|
778
798
|
jsBefore?: string;
|
|
779
799
|
jsAfter?: string;
|
|
800
|
+
skinCss?: string;
|
|
801
|
+
skinJs?: string;
|
|
780
802
|
}
|
|
781
803
|
/**
|
|
782
804
|
* Update manifest files to include injected assets
|
|
@@ -840,6 +862,8 @@ interface PatchOptions {
|
|
|
840
862
|
jsAfterContent?: string;
|
|
841
863
|
/** Skip fetching remote files even if fetchFallbacks is enabled in config */
|
|
842
864
|
skipFetch?: boolean;
|
|
865
|
+
/** Optional skin name — per-call override for config.skin */
|
|
866
|
+
skin?: string;
|
|
843
867
|
}
|
|
844
868
|
/**
|
|
845
869
|
* Main Patcher class for patching e-learning course packages
|
|
@@ -912,7 +936,7 @@ declare class Patcher {
|
|
|
912
936
|
* @param buffer - Input ZIP file as Buffer
|
|
913
937
|
* @returns Patched ZIP file as Buffer
|
|
914
938
|
*/
|
|
915
|
-
patchBuffer(buffer: Buffer): Promise<Buffer>;
|
|
939
|
+
patchBuffer(buffer: Buffer, options?: PatchOptions): Promise<Buffer>;
|
|
916
940
|
}
|
|
917
941
|
|
|
918
942
|
/**
|
|
@@ -1030,6 +1054,8 @@ interface JsBeforeOptions {
|
|
|
1030
1054
|
metadata: CourseMetadata | null;
|
|
1031
1055
|
/** LRS Bridge configuration */
|
|
1032
1056
|
lrsBridge: LrsBridgeOptions;
|
|
1057
|
+
/** Optional skin name */
|
|
1058
|
+
skin?: string;
|
|
1033
1059
|
}
|
|
1034
1060
|
/**
|
|
1035
1061
|
* Generate the blocking JS loader for the "before" slot
|
package/dist/index.js
CHANGED
|
@@ -106,7 +106,9 @@ var PatchAdamsConfigSchema = z.object({
|
|
|
106
106
|
/** LRS Bridge configuration for xAPI/LRS communication with Bravais */
|
|
107
107
|
lrsBridge: LrsBridgeConfigSchema.default({}),
|
|
108
108
|
/** Plugin configurations - each plugin is keyed by its name */
|
|
109
|
-
plugins: PluginsConfigSchema
|
|
109
|
+
plugins: PluginsConfigSchema,
|
|
110
|
+
/** Optional skin name — adds 'pa-skinned' + skin class to <html>, loads skin CSS/JS after core files */
|
|
111
|
+
skin: z.string().min(1).optional()
|
|
110
112
|
});
|
|
111
113
|
|
|
112
114
|
// src/config/defaults.ts
|
|
@@ -596,6 +598,7 @@ function generateLrsBridgeCode(options) {
|
|
|
596
598
|
sessionId: null, // Session UUID
|
|
597
599
|
launchTime: null, // ISO timestamp of course launch
|
|
598
600
|
courseAttemptId: null, // Unique ID for this course attempt session (Xyleme format)
|
|
601
|
+
employeeId: null, // Employee ID from employee lookup
|
|
599
602
|
// Statistics
|
|
600
603
|
stats: {
|
|
601
604
|
statementsSent: 0,
|
|
@@ -1426,6 +1429,11 @@ function generateLrsBridgeCode(options) {
|
|
|
1426
1429
|
};
|
|
1427
1430
|
log('Updated actor account: bravaisUserId=' + employeeData.bravaisUserId + ', homePage=' + actor.account.homePage + ' (was: ' + originalAccountName + ')');
|
|
1428
1431
|
}
|
|
1432
|
+
|
|
1433
|
+
// Expose employee ID on LRS object for plugins (e.g. feedback)
|
|
1434
|
+
if (employeeData.employeeId) {
|
|
1435
|
+
LRS.employeeId = employeeData.employeeId;
|
|
1436
|
+
}
|
|
1429
1437
|
}
|
|
1430
1438
|
callback(actor);
|
|
1431
1439
|
});
|
|
@@ -4371,7 +4379,7 @@ function escapeJs(str) {
|
|
|
4371
4379
|
return str.replace(/\\/g, "\\\\").replace(/'/g, "\\'").replace(/"/g, '\\"').replace(/\n/g, "\\n").replace(/\r/g, "\\r");
|
|
4372
4380
|
}
|
|
4373
4381
|
function generateJsBeforeLoader(options) {
|
|
4374
|
-
const { remoteUrl, localPath, htmlClass, loadingClass, metadata, lrsBridge } = options;
|
|
4382
|
+
const { remoteUrl, localPath, htmlClass, loadingClass, metadata, lrsBridge, skin } = options;
|
|
4375
4383
|
const lrsBridgeCode = generateLrsBridgeCode(lrsBridge);
|
|
4376
4384
|
const courseLines = [];
|
|
4377
4385
|
if (metadata) {
|
|
@@ -4414,12 +4422,13 @@ ${courseLines.join("\n")}
|
|
|
4414
4422
|
window.pa_patcher = window.pa_patcher || {
|
|
4415
4423
|
version: '1.0.25',
|
|
4416
4424
|
htmlClass: '${htmlClass}',
|
|
4417
|
-
loadingClass: '${loadingClass}',${
|
|
4425
|
+
loadingClass: '${loadingClass}',${skin ? `
|
|
4426
|
+
skin: '${escapeJs(skin)}',` : ""}${courseBlock}
|
|
4418
4427
|
loaded: {
|
|
4419
4428
|
cssBefore: false,
|
|
4420
4429
|
cssAfter: false,
|
|
4421
4430
|
jsBefore: false,
|
|
4422
|
-
jsAfter: false
|
|
4431
|
+
jsAfter: false${skin ? ",\n skinCss: false,\n skinJs: false" : ""}
|
|
4423
4432
|
}
|
|
4424
4433
|
};
|
|
4425
4434
|
|
|
@@ -4540,7 +4549,8 @@ function buildJsBeforeOptions(config, metadata) {
|
|
|
4540
4549
|
htmlClass: config.htmlClass,
|
|
4541
4550
|
loadingClass: config.loadingClass,
|
|
4542
4551
|
metadata: metadata ?? null,
|
|
4543
|
-
lrsBridge
|
|
4552
|
+
lrsBridge,
|
|
4553
|
+
skin: config.skin
|
|
4544
4554
|
};
|
|
4545
4555
|
}
|
|
4546
4556
|
|
|
@@ -4663,6 +4673,196 @@ function buildJsAfterOptions(config) {
|
|
|
4663
4673
|
};
|
|
4664
4674
|
}
|
|
4665
4675
|
|
|
4676
|
+
// src/templates/skin-css.ts
|
|
4677
|
+
function generateSkinCssLoader(options) {
|
|
4678
|
+
const { remoteUrl, localPath, timeout } = options;
|
|
4679
|
+
return `<!-- === PATCH-ADAMS: SKIN CSS (async with fallback) === -->
|
|
4680
|
+
<script data-pa="skin-css-loader">
|
|
4681
|
+
(function() {
|
|
4682
|
+
'use strict';
|
|
4683
|
+
var REMOTE_URL = "${remoteUrl}";
|
|
4684
|
+
var LOCAL_PATH = "${localPath}";
|
|
4685
|
+
var TIMEOUT = ${timeout};
|
|
4686
|
+
|
|
4687
|
+
function loadCSS(url, onSuccess, onError) {
|
|
4688
|
+
var link = document.createElement('link');
|
|
4689
|
+
link.rel = 'stylesheet';
|
|
4690
|
+
link.href = url;
|
|
4691
|
+
link.setAttribute('data-pa', 'skin-css');
|
|
4692
|
+
|
|
4693
|
+
link.onload = function() {
|
|
4694
|
+
if (onSuccess) onSuccess();
|
|
4695
|
+
};
|
|
4696
|
+
|
|
4697
|
+
link.onerror = function() {
|
|
4698
|
+
if (onError) onError();
|
|
4699
|
+
};
|
|
4700
|
+
|
|
4701
|
+
document.head.appendChild(link);
|
|
4702
|
+
return link;
|
|
4703
|
+
}
|
|
4704
|
+
|
|
4705
|
+
function loadCSSWithFallback() {
|
|
4706
|
+
var loaded = false;
|
|
4707
|
+
var timeoutId;
|
|
4708
|
+
|
|
4709
|
+
// Try remote first
|
|
4710
|
+
var remoteLink = loadCSS(
|
|
4711
|
+
REMOTE_URL,
|
|
4712
|
+
function() {
|
|
4713
|
+
if (loaded) return;
|
|
4714
|
+
loaded = true;
|
|
4715
|
+
clearTimeout(timeoutId);
|
|
4716
|
+
console.log('[PA-Patcher] Skin CSS loaded from remote:', REMOTE_URL);
|
|
4717
|
+
},
|
|
4718
|
+
function() {
|
|
4719
|
+
if (loaded) return;
|
|
4720
|
+
loaded = true;
|
|
4721
|
+
clearTimeout(timeoutId);
|
|
4722
|
+
loadLocalFallback();
|
|
4723
|
+
}
|
|
4724
|
+
);
|
|
4725
|
+
|
|
4726
|
+
// Timeout fallback
|
|
4727
|
+
timeoutId = setTimeout(function() {
|
|
4728
|
+
if (loaded) return;
|
|
4729
|
+
loaded = true;
|
|
4730
|
+
console.warn('[PA-Patcher] Skin CSS timed out, using local fallback');
|
|
4731
|
+
if (remoteLink.parentNode) {
|
|
4732
|
+
document.head.removeChild(remoteLink);
|
|
4733
|
+
}
|
|
4734
|
+
loadLocalFallback();
|
|
4735
|
+
}, TIMEOUT);
|
|
4736
|
+
}
|
|
4737
|
+
|
|
4738
|
+
function loadLocalFallback() {
|
|
4739
|
+
loadCSS(
|
|
4740
|
+
LOCAL_PATH,
|
|
4741
|
+
function() {
|
|
4742
|
+
console.log('[PA-Patcher] Skin CSS loaded from local fallback:', LOCAL_PATH);
|
|
4743
|
+
},
|
|
4744
|
+
function() {
|
|
4745
|
+
console.error('[PA-Patcher] Skin CSS failed to load from both remote and local');
|
|
4746
|
+
}
|
|
4747
|
+
);
|
|
4748
|
+
}
|
|
4749
|
+
|
|
4750
|
+
// Execute immediately
|
|
4751
|
+
loadCSSWithFallback();
|
|
4752
|
+
})();
|
|
4753
|
+
</script>`;
|
|
4754
|
+
}
|
|
4755
|
+
function buildSkinCssOptions(config) {
|
|
4756
|
+
if (!config.skin) return null;
|
|
4757
|
+
return {
|
|
4758
|
+
remoteUrl: `${config.remoteDomain}/skin/${config.skin}.css`,
|
|
4759
|
+
localPath: `skin/${config.skin}.css`,
|
|
4760
|
+
timeout: config.cssAfter.timeout
|
|
4761
|
+
// reuse cssAfter timeout
|
|
4762
|
+
};
|
|
4763
|
+
}
|
|
4764
|
+
|
|
4765
|
+
// src/templates/skin-js.ts
|
|
4766
|
+
function generateSkinJsLoader(options) {
|
|
4767
|
+
const { remoteUrl, localPath, timeout } = options;
|
|
4768
|
+
return `<!-- === PATCH-ADAMS: SKIN JS (async with fallback) === -->
|
|
4769
|
+
<script data-pa="skin-js-loader">
|
|
4770
|
+
(function() {
|
|
4771
|
+
'use strict';
|
|
4772
|
+
var REMOTE_URL = "${remoteUrl}";
|
|
4773
|
+
var LOCAL_PATH = "${localPath}";
|
|
4774
|
+
var TIMEOUT = ${timeout};
|
|
4775
|
+
|
|
4776
|
+
function loadJS(url, onSuccess, onError) {
|
|
4777
|
+
var script = document.createElement('script');
|
|
4778
|
+
script.src = url;
|
|
4779
|
+
script.async = true;
|
|
4780
|
+
script.setAttribute('data-pa', 'skin-js');
|
|
4781
|
+
|
|
4782
|
+
script.onload = function() {
|
|
4783
|
+
if (onSuccess) onSuccess();
|
|
4784
|
+
};
|
|
4785
|
+
|
|
4786
|
+
script.onerror = function() {
|
|
4787
|
+
if (onError) onError();
|
|
4788
|
+
};
|
|
4789
|
+
|
|
4790
|
+
document.body.appendChild(script);
|
|
4791
|
+
return script;
|
|
4792
|
+
}
|
|
4793
|
+
|
|
4794
|
+
function loadJSWithFallback() {
|
|
4795
|
+
var loaded = false;
|
|
4796
|
+
var timeoutId;
|
|
4797
|
+
|
|
4798
|
+
// Try remote first
|
|
4799
|
+
var remoteScript = loadJS(
|
|
4800
|
+
REMOTE_URL,
|
|
4801
|
+
function() {
|
|
4802
|
+
if (loaded) return;
|
|
4803
|
+
loaded = true;
|
|
4804
|
+
clearTimeout(timeoutId);
|
|
4805
|
+
console.log('[PA-Patcher] Skin JS loaded from remote:', REMOTE_URL);
|
|
4806
|
+
if (window.pa_patcher && window.pa_patcher.loaded) {
|
|
4807
|
+
window.pa_patcher.loaded.skinJs = true;
|
|
4808
|
+
}
|
|
4809
|
+
},
|
|
4810
|
+
function() {
|
|
4811
|
+
if (loaded) return;
|
|
4812
|
+
loaded = true;
|
|
4813
|
+
clearTimeout(timeoutId);
|
|
4814
|
+
loadLocalFallback();
|
|
4815
|
+
}
|
|
4816
|
+
);
|
|
4817
|
+
|
|
4818
|
+
// Timeout fallback
|
|
4819
|
+
timeoutId = setTimeout(function() {
|
|
4820
|
+
if (loaded) return;
|
|
4821
|
+
loaded = true;
|
|
4822
|
+
console.warn('[PA-Patcher] Skin JS timed out, using local fallback');
|
|
4823
|
+
if (remoteScript.parentNode) {
|
|
4824
|
+
document.body.removeChild(remoteScript);
|
|
4825
|
+
}
|
|
4826
|
+
loadLocalFallback();
|
|
4827
|
+
}, TIMEOUT);
|
|
4828
|
+
}
|
|
4829
|
+
|
|
4830
|
+
function loadLocalFallback() {
|
|
4831
|
+
loadJS(
|
|
4832
|
+
LOCAL_PATH,
|
|
4833
|
+
function() {
|
|
4834
|
+
console.log('[PA-Patcher] Skin JS loaded from local fallback:', LOCAL_PATH);
|
|
4835
|
+
if (window.pa_patcher && window.pa_patcher.loaded) {
|
|
4836
|
+
window.pa_patcher.loaded.skinJs = true;
|
|
4837
|
+
}
|
|
4838
|
+
},
|
|
4839
|
+
function() {
|
|
4840
|
+
console.error('[PA-Patcher] Skin JS failed to load from both remote and local');
|
|
4841
|
+
}
|
|
4842
|
+
);
|
|
4843
|
+
}
|
|
4844
|
+
|
|
4845
|
+
// Execute after DOM is ready
|
|
4846
|
+
if (document.readyState === 'loading') {
|
|
4847
|
+
document.addEventListener('DOMContentLoaded', function() {
|
|
4848
|
+
setTimeout(loadJSWithFallback, 150);
|
|
4849
|
+
});
|
|
4850
|
+
} else {
|
|
4851
|
+
setTimeout(loadJSWithFallback, 150);
|
|
4852
|
+
}
|
|
4853
|
+
})();
|
|
4854
|
+
</script>`;
|
|
4855
|
+
}
|
|
4856
|
+
function buildSkinJsOptions(config) {
|
|
4857
|
+
if (!config.skin) return null;
|
|
4858
|
+
return {
|
|
4859
|
+
remoteUrl: `${config.remoteDomain}/skin/${config.skin}.js`,
|
|
4860
|
+
localPath: `skin/${config.skin}.js`,
|
|
4861
|
+
timeout: config.jsAfter.timeout
|
|
4862
|
+
// reuse jsAfter timeout
|
|
4863
|
+
};
|
|
4864
|
+
}
|
|
4865
|
+
|
|
4666
4866
|
// src/patcher/html-injector.ts
|
|
4667
4867
|
var HtmlInjector = class {
|
|
4668
4868
|
config;
|
|
@@ -4726,6 +4926,10 @@ var HtmlInjector = class {
|
|
|
4726
4926
|
if (this.config.jsAfter.enabled) {
|
|
4727
4927
|
result = this.injectJsAfter(result);
|
|
4728
4928
|
}
|
|
4929
|
+
if (this.config.skin) {
|
|
4930
|
+
result = this.injectSkinCss(result);
|
|
4931
|
+
result = this.injectSkinJs(result);
|
|
4932
|
+
}
|
|
4729
4933
|
if (this.pluginAssets) {
|
|
4730
4934
|
result = this.injectPluginAssets(result);
|
|
4731
4935
|
}
|
|
@@ -4772,8 +4976,8 @@ ${pluginJs}
|
|
|
4772
4976
|
* Also adds data attributes for course metadata
|
|
4773
4977
|
*/
|
|
4774
4978
|
addHtmlAttributes(html) {
|
|
4775
|
-
const { htmlClass, loadingClass } = this.config;
|
|
4776
|
-
const classes = `${htmlClass} ${loadingClass}`;
|
|
4979
|
+
const { htmlClass, loadingClass, skin } = this.config;
|
|
4980
|
+
const classes = `${htmlClass} ${loadingClass}${skin ? ` pa-skinned ${skin}` : ""}`;
|
|
4777
4981
|
const dataAttrs = [];
|
|
4778
4982
|
if (this.metadata) {
|
|
4779
4983
|
dataAttrs.push(`data-pa-course-id="${this.escapeAttr(this.metadata.courseId)}"`);
|
|
@@ -4782,6 +4986,9 @@ ${pluginJs}
|
|
|
4782
4986
|
dataAttrs.push(`data-pa-title="${this.escapeAttr(this.metadata.title)}"`);
|
|
4783
4987
|
}
|
|
4784
4988
|
}
|
|
4989
|
+
if (skin) {
|
|
4990
|
+
dataAttrs.push(`data-pa-skin="${this.escapeAttr(skin)}"`);
|
|
4991
|
+
}
|
|
4785
4992
|
const dataAttrString = dataAttrs.length > 0 ? " " + dataAttrs.join(" ") : "";
|
|
4786
4993
|
const htmlTagPattern = /<html([^>]*)>/i;
|
|
4787
4994
|
const match = html.match(htmlTagPattern);
|
|
@@ -4860,6 +5067,26 @@ ${loader}`);
|
|
|
4860
5067
|
${loader}`);
|
|
4861
5068
|
}
|
|
4862
5069
|
return html.replace(/<\/body>/i, `${loader}
|
|
5070
|
+
</body>`);
|
|
5071
|
+
}
|
|
5072
|
+
/**
|
|
5073
|
+
* Inject Skin CSS loader after CSS After (before </head>)
|
|
5074
|
+
*/
|
|
5075
|
+
injectSkinCss(html) {
|
|
5076
|
+
const options = buildSkinCssOptions(this.config);
|
|
5077
|
+
if (!options) return html;
|
|
5078
|
+
const loader = generateSkinCssLoader(options);
|
|
5079
|
+
return html.replace(/<\/head>/i, `${loader}
|
|
5080
|
+
</head>`);
|
|
5081
|
+
}
|
|
5082
|
+
/**
|
|
5083
|
+
* Inject Skin JS loader after JS After (before </body>)
|
|
5084
|
+
*/
|
|
5085
|
+
injectSkinJs(html) {
|
|
5086
|
+
const options = buildSkinJsOptions(this.config);
|
|
5087
|
+
if (!options) return html;
|
|
5088
|
+
const loader = generateSkinJsLoader(options);
|
|
5089
|
+
return html.replace(/<\/body>/i, `${loader}
|
|
4863
5090
|
</body>`);
|
|
4864
5091
|
}
|
|
4865
5092
|
};
|
|
@@ -4903,6 +5130,10 @@ var StorylineHtmlInjector = class {
|
|
|
4903
5130
|
if (this.config.jsAfter.enabled) {
|
|
4904
5131
|
result = this.injectJsAfter(result);
|
|
4905
5132
|
}
|
|
5133
|
+
if (this.config.skin) {
|
|
5134
|
+
result = this.injectSkinCss(result);
|
|
5135
|
+
result = this.injectSkinJs(result);
|
|
5136
|
+
}
|
|
4906
5137
|
if (this.pluginAssets) {
|
|
4907
5138
|
result = this.injectPluginAssets(result);
|
|
4908
5139
|
}
|
|
@@ -4947,8 +5178,8 @@ ${pluginJs}
|
|
|
4947
5178
|
* Also adds data attributes for course metadata
|
|
4948
5179
|
*/
|
|
4949
5180
|
addHtmlAttributes(html) {
|
|
4950
|
-
const { htmlClass, loadingClass } = this.config;
|
|
4951
|
-
const classes = `${htmlClass} ${loadingClass}`;
|
|
5181
|
+
const { htmlClass, loadingClass, skin } = this.config;
|
|
5182
|
+
const classes = `${htmlClass} ${loadingClass}${skin ? ` pa-skinned ${skin}` : ""}`;
|
|
4952
5183
|
const dataAttrs = [];
|
|
4953
5184
|
if (this.metadata) {
|
|
4954
5185
|
dataAttrs.push(`data-pa-course-id="${this.escapeAttr(this.metadata.courseId)}"`);
|
|
@@ -4958,6 +5189,9 @@ ${pluginJs}
|
|
|
4958
5189
|
dataAttrs.push(`data-pa-title="${this.escapeAttr(this.metadata.title)}"`);
|
|
4959
5190
|
}
|
|
4960
5191
|
}
|
|
5192
|
+
if (skin) {
|
|
5193
|
+
dataAttrs.push(`data-pa-skin="${this.escapeAttr(skin)}"`);
|
|
5194
|
+
}
|
|
4961
5195
|
const dataAttrString = dataAttrs.length > 0 ? " " + dataAttrs.join(" ") : "";
|
|
4962
5196
|
const htmlTagPattern = /<html([^>]*)>/i;
|
|
4963
5197
|
const match = html.match(htmlTagPattern);
|
|
@@ -5035,6 +5269,26 @@ ${loader}`);
|
|
|
5035
5269
|
const options = buildJsAfterOptions(this.config);
|
|
5036
5270
|
const loader = generateJsAfterLoader(options);
|
|
5037
5271
|
return html.replace(/<\/body>/i, `${loader}
|
|
5272
|
+
</body>`);
|
|
5273
|
+
}
|
|
5274
|
+
/**
|
|
5275
|
+
* Inject Skin CSS loader after CSS After (before </head>)
|
|
5276
|
+
*/
|
|
5277
|
+
injectSkinCss(html) {
|
|
5278
|
+
const options = buildSkinCssOptions(this.config);
|
|
5279
|
+
if (!options) return html;
|
|
5280
|
+
const loader = generateSkinCssLoader(options);
|
|
5281
|
+
return html.replace(/<\/head>/i, `${loader}
|
|
5282
|
+
</head>`);
|
|
5283
|
+
}
|
|
5284
|
+
/**
|
|
5285
|
+
* Inject Skin JS loader after JS After (before </body>)
|
|
5286
|
+
*/
|
|
5287
|
+
injectSkinJs(html) {
|
|
5288
|
+
const options = buildSkinJsOptions(this.config);
|
|
5289
|
+
if (!options) return html;
|
|
5290
|
+
const loader = generateSkinJsLoader(options);
|
|
5291
|
+
return html.replace(/<\/body>/i, `${loader}
|
|
5038
5292
|
</body>`);
|
|
5039
5293
|
}
|
|
5040
5294
|
};
|
|
@@ -5085,7 +5339,9 @@ var ManifestUpdater = class {
|
|
|
5085
5339
|
paths.cssBefore,
|
|
5086
5340
|
paths.cssAfter,
|
|
5087
5341
|
paths.jsBefore,
|
|
5088
|
-
paths.jsAfter
|
|
5342
|
+
paths.jsAfter,
|
|
5343
|
+
paths.skinCss,
|
|
5344
|
+
paths.skinJs
|
|
5089
5345
|
].filter(Boolean);
|
|
5090
5346
|
if (filesToAdd.length === 0) {
|
|
5091
5347
|
return [];
|
|
@@ -5875,6 +6131,20 @@ var Patcher = class {
|
|
|
5875
6131
|
})
|
|
5876
6132
|
);
|
|
5877
6133
|
}
|
|
6134
|
+
if (this.config.skin) {
|
|
6135
|
+
const skinCssUrl = `${remoteDomain}/skin/${this.config.skin}.css`;
|
|
6136
|
+
fetchPromises.push(
|
|
6137
|
+
this.fetchFile(skinCssUrl).then((content) => {
|
|
6138
|
+
fetched.skinCss = content;
|
|
6139
|
+
})
|
|
6140
|
+
);
|
|
6141
|
+
const skinJsUrl = `${remoteDomain}/skin/${this.config.skin}.js`;
|
|
6142
|
+
fetchPromises.push(
|
|
6143
|
+
this.fetchFile(skinJsUrl).then((content) => {
|
|
6144
|
+
fetched.skinJs = content;
|
|
6145
|
+
})
|
|
6146
|
+
);
|
|
6147
|
+
}
|
|
5878
6148
|
await Promise.all(fetchPromises);
|
|
5879
6149
|
return fetched;
|
|
5880
6150
|
}
|
|
@@ -5958,6 +6228,16 @@ var Patcher = class {
|
|
|
5958
6228
|
console.log(`[Patcher] Wrote generated UUID to manifest identifier`);
|
|
5959
6229
|
}
|
|
5960
6230
|
}
|
|
6231
|
+
const effectiveSkin = options.skin || this.config.skin;
|
|
6232
|
+
if (options.skin && options.skin !== this.config.skin) {
|
|
6233
|
+
this.config.skin = options.skin;
|
|
6234
|
+
this.riseHtmlInjector = new HtmlInjector(this.config);
|
|
6235
|
+
this.storylineHtmlInjector = new StorylineHtmlInjector(this.config);
|
|
6236
|
+
console.log(`[Patcher] Using per-call skin override: ${options.skin}`);
|
|
6237
|
+
}
|
|
6238
|
+
if (effectiveSkin) {
|
|
6239
|
+
console.log(`[Patcher] Skin: ${effectiveSkin}`);
|
|
6240
|
+
}
|
|
5961
6241
|
const htmlInjector = this.getHtmlInjector(toolInfo.tool);
|
|
5962
6242
|
htmlInjector.setMetadata(metadata);
|
|
5963
6243
|
let fetchedFallbacks = {};
|
|
@@ -6086,7 +6366,11 @@ var Patcher = class {
|
|
|
6086
6366
|
buildManifestPaths(addedFiles) {
|
|
6087
6367
|
const paths = {};
|
|
6088
6368
|
for (const filePath of addedFiles) {
|
|
6089
|
-
if (filePath.endsWith(this.config.
|
|
6369
|
+
if (this.config.skin && filePath.endsWith(`skin/${this.config.skin}.css`)) {
|
|
6370
|
+
paths.skinCss = filePath;
|
|
6371
|
+
} else if (this.config.skin && filePath.endsWith(`skin/${this.config.skin}.js`)) {
|
|
6372
|
+
paths.skinJs = filePath;
|
|
6373
|
+
} else if (filePath.endsWith(this.config.cssBefore.filename)) {
|
|
6090
6374
|
paths.cssBefore = filePath;
|
|
6091
6375
|
} else if (filePath.endsWith(this.config.cssAfter.filename)) {
|
|
6092
6376
|
paths.cssAfter = filePath;
|
|
@@ -6170,6 +6454,22 @@ var Patcher = class {
|
|
|
6170
6454
|
added.push(path);
|
|
6171
6455
|
if (fetched.jsAfter) console.log(`[Patcher] Using fetched content for ${path}`);
|
|
6172
6456
|
}
|
|
6457
|
+
if (this.config.skin) {
|
|
6458
|
+
const skinCssPath = `${basePath}skin/${this.config.skin}.css`;
|
|
6459
|
+
const skinCssContent = fetched.skinCss ?? `/* PA-Patcher: Skin CSS (${this.config.skin}) */
|
|
6460
|
+
`;
|
|
6461
|
+
zip.addFile(skinCssPath, Buffer.from(skinCssContent, "utf-8"));
|
|
6462
|
+
added.push(skinCssPath);
|
|
6463
|
+
if (fetched.skinCss) console.log(`[Patcher] Using fetched skin CSS for ${skinCssPath}`);
|
|
6464
|
+
else console.log(`[Patcher] Added placeholder skin CSS: ${skinCssPath}`);
|
|
6465
|
+
const skinJsPath = `${basePath}skin/${this.config.skin}.js`;
|
|
6466
|
+
const skinJsContent = fetched.skinJs ?? `// PA-Patcher: Skin JS (${this.config.skin})
|
|
6467
|
+
`;
|
|
6468
|
+
zip.addFile(skinJsPath, Buffer.from(skinJsContent, "utf-8"));
|
|
6469
|
+
added.push(skinJsPath);
|
|
6470
|
+
if (fetched.skinJs) console.log(`[Patcher] Using fetched skin JS for ${skinJsPath}`);
|
|
6471
|
+
else console.log(`[Patcher] Added placeholder skin JS: ${skinJsPath}`);
|
|
6472
|
+
}
|
|
6173
6473
|
return added;
|
|
6174
6474
|
}
|
|
6175
6475
|
/**
|
|
@@ -6184,9 +6484,9 @@ var Patcher = class {
|
|
|
6184
6484
|
* @param buffer - Input ZIP file as Buffer
|
|
6185
6485
|
* @returns Patched ZIP file as Buffer
|
|
6186
6486
|
*/
|
|
6187
|
-
async patchBuffer(buffer) {
|
|
6487
|
+
async patchBuffer(buffer, options) {
|
|
6188
6488
|
console.log(`[Patcher] patchBuffer called with ${buffer.length} bytes`);
|
|
6189
|
-
const { buffer: patchedBuffer, result } = await this.patch(buffer);
|
|
6489
|
+
const { buffer: patchedBuffer, result } = await this.patch(buffer, options);
|
|
6190
6490
|
console.log(`[Patcher] Patch result:`, JSON.stringify(result, null, 2));
|
|
6191
6491
|
console.log(`[Patcher] Returning ${patchedBuffer.length} bytes`);
|
|
6192
6492
|
return patchedBuffer;
|