@pie-players/pie-players-shared 0.3.73 → 0.3.74
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/README.md +27 -1
- package/dist/components/vendor/nds/README.md +7 -5
- package/dist/config/profile.d.ts +0 -2
- package/dist/config/profile.js +2 -8
- package/dist/i18n/index.d.ts +2 -3
- package/dist/i18n/index.js +2 -3
- package/dist/i18n/messages/en-US.d.ts +10 -0
- package/dist/i18n/messages/en-US.js +12 -2
- package/dist/i18n/messages/nl-NL.d.ts +6 -0
- package/dist/i18n/messages/nl-NL.js +8 -2
- package/dist/i18n/provider.d.ts +4 -5
- package/dist/i18n/provider.js +4 -5
- package/dist/index.d.ts +4 -2
- package/dist/index.js +3 -1
- package/dist/instrumentation/index.d.ts +5 -4
- package/dist/instrumentation/index.js +5 -4
- package/dist/instrumentation/new-relic-agent.d.ts +20 -0
- package/dist/instrumentation/new-relic-agent.js +24 -0
- package/dist/instrumentation/providers/BaseInstrumentationProvider.d.ts +5 -4
- package/dist/instrumentation/providers/NewRelicInstrumentationProvider.d.ts +7 -7
- package/dist/instrumentation/providers/NewRelicInstrumentationProvider.js +21 -32
- package/dist/instrumentation/types.d.ts +7 -3
- package/dist/loader-config.d.ts +16 -6
- package/dist/loaders/controller-loading.d.ts +14 -0
- package/dist/loaders/controller-loading.js +12 -0
- package/dist/loaders/defaults.d.ts +10 -0
- package/dist/loaders/defaults.js +10 -0
- package/dist/loaders/element-loader.d.ts +20 -8
- package/dist/loaders/element-loader.js +102 -36
- package/dist/loaders/element-package-policy.d.ts +2 -0
- package/dist/loaders/element-package-policy.js +2 -1
- package/dist/loaders/esm-adapter.d.ts +18 -0
- package/dist/loaders/esm-adapter.js +617 -102
- package/dist/loaders/iife-adapter.d.ts +0 -5
- package/dist/loaders/iife-adapter.js +3 -9
- package/dist/loaders/index.d.ts +10 -3
- package/dist/loaders/index.js +8 -3
- package/dist/loaders/mathjax-version-conflict.d.ts +17 -0
- package/dist/loaders/mathjax-version-conflict.js +51 -0
- package/dist/loaders/module-shim.d.ts +12 -0
- package/dist/loaders/module-shim.js +12 -0
- package/dist/loaders/preloaded-alignment.d.ts +24 -0
- package/dist/loaders/preloaded-alignment.js +57 -0
- package/dist/loaders/preloaded-authored-tags.d.ts +26 -0
- package/dist/loaders/preloaded-authored-tags.js +69 -0
- package/dist/loaders/preloaded-registration.d.ts +56 -0
- package/dist/loaders/preloaded-registration.js +129 -0
- package/dist/media/index.d.ts +69 -0
- package/dist/media/index.js +194 -0
- package/dist/pie/authoring.d.ts +1 -1
- package/dist/pie/authoring.js +25 -18
- package/dist/pie/element-announcements.d.ts +39 -0
- package/dist/pie/element-announcements.js +67 -0
- package/dist/pie/element-observer.d.ts +3 -1
- package/dist/pie/element-observer.js +1 -0
- package/dist/pie/index.d.ts +4 -4
- package/dist/pie/index.js +5 -3
- package/dist/pie/initialization.js +13 -22
- package/dist/pie/initialize-element.d.ts +3 -0
- package/dist/pie/initialize-element.js +3 -4
- package/dist/pie/instrumentation-event-map.js +2 -9
- package/dist/pie/instrumentation-provider-resolution.d.ts +2 -1
- package/dist/pie/instrumentation-provider-resolution.js +0 -1
- package/dist/pie/item-session-contract.d.ts +8 -0
- package/dist/pie/item-session-contract.js +30 -9
- package/dist/pie/markup-math.d.ts +20 -0
- package/dist/pie/markup-math.js +118 -0
- package/dist/pie/math-rendering.d.ts +10 -21
- package/dist/pie/math-rendering.js +50 -35
- package/dist/pie/registry.d.ts +13 -1
- package/dist/pie/registry.js +39 -1
- package/dist/pie/resource-monitor.d.ts +6 -33
- package/dist/pie/resource-monitor.js +14 -213
- package/dist/pie/scoring.d.ts +11 -2
- package/dist/pie/scoring.js +18 -16
- package/dist/pie/session-commit.d.ts +16 -0
- package/dist/pie/session-commit.js +33 -5
- package/dist/pie/session-snapshot.d.ts +80 -0
- package/dist/pie/session-snapshot.js +166 -0
- package/dist/pie/tag-names.d.ts +1 -0
- package/dist/pie/tag-names.js +1 -0
- package/dist/pie/types.d.ts +4 -5
- package/dist/pie/types.js +1 -0
- package/dist/pie/updates.d.ts +2 -2
- package/dist/pie/updates.js +33 -15
- package/dist/pie/versioned-tag.d.ts +4 -0
- package/dist/pie/versioned-tag.js +4 -0
- package/dist/security/index.d.ts +2 -3
- package/dist/security/index.js +2 -3
- package/dist/security/sanitize-item-markup.d.ts +0 -2
- package/dist/security/sanitize-item-markup.js +0 -4
- package/dist/security/sanitize-svg-icon.d.ts +0 -2
- package/dist/security/sanitize-svg-icon.js +0 -5
- package/dist/security/wrap-overwide.d.ts +5 -0
- package/dist/security/wrap-overwide.js +16 -0
- package/dist/timed-media/policy.js +5 -16
- package/dist/types/index.d.ts +30 -311
- package/dist/types/index.js +0 -68
- package/dist/ui/attribute-coercion.d.ts +12 -0
- package/dist/ui/attribute-coercion.js +33 -0
- package/dist/ui/content-styles.d.ts +18 -16
- package/dist/ui/content-styles.js +67 -36
- package/dist/ui/overlay-containment.d.ts +31 -0
- package/dist/ui/overlay-containment.js +63 -0
- package/dist/ui/overlay-placement.d.ts +110 -0
- package/dist/ui/overlay-placement.js +222 -0
- package/dist/ui/pointer-gesture.d.ts +32 -0
- package/dist/ui/pointer-gesture.js +56 -0
- package/dist/ui/pointer-rotate.d.ts +29 -0
- package/dist/ui/pointer-rotate.js +39 -0
- package/package.json +21 -17
- package/dist/pie/component-context.d.ts +0 -21
- package/dist/pie/component-context.js +0 -29
- package/dist/pie/player-initializer.d.ts +0 -54
- package/dist/pie/player-initializer.js +0 -122
- package/dist/security/wrap-model-rich-content.d.ts +0 -1
- package/dist/security/wrap-model-rich-content.js +0 -40
- package/dist/server/npm-auth-env.d.ts +0 -13
- package/dist/server/npm-auth-env.js +0 -64
- package/dist/server/npm-registry.d.ts +0 -7
- package/dist/server/npm-registry.js +0 -59
- package/dist/ui/debug-panel-persistence.d.ts +0 -17
- package/dist/ui/debug-panel-persistence.js +0 -37
|
@@ -11,7 +11,7 @@ import { editorPostFix } from "../types/index.js";
|
|
|
11
11
|
import { initializePieElement } from "./initialize-element.js";
|
|
12
12
|
import { createPieLogger, isGlobalDebugEnabled } from "./logger.js";
|
|
13
13
|
import { initializeMathRendering } from "./math-rendering.js";
|
|
14
|
-
import { pieRegistry } from "./registry.js";
|
|
14
|
+
import { pieRegistry, writeRegistryEntry } from "./registry.js";
|
|
15
15
|
import { defineCustomElementSafely } from "./custom-element-define.js";
|
|
16
16
|
import { validateCustomElementTag } from "./tag-names.js";
|
|
17
17
|
import { BundleType, isCustomElementConstructor, isPieAvailable, Status, } from "./types.js";
|
|
@@ -38,6 +38,7 @@ const updateRegisteredElement = (elementTagName, config, session, options, omitE
|
|
|
38
38
|
config,
|
|
39
39
|
session,
|
|
40
40
|
...(omitEnv ? {} : { env: options.env }),
|
|
41
|
+
bundleType: options.bundleType,
|
|
41
42
|
container: options.container,
|
|
42
43
|
...(options.eventListeners?.[elementTagName] && {
|
|
43
44
|
eventListeners: options.eventListeners[elementTagName],
|
|
@@ -119,30 +120,21 @@ const registerPieElementsFromBundle = (elementModule, config, session, registry,
|
|
|
119
120
|
: [],
|
|
120
121
|
bundleType: options.bundleType,
|
|
121
122
|
});
|
|
122
|
-
// Validate controller presence based on bundle type
|
|
123
|
-
if (!elementData.controller) {
|
|
124
|
-
if (options.bundleType === BundleType.clientPlayer) {
|
|
125
|
-
logger.error(`[registerPieElementsFromBundle] ❌ CRITICAL: No controller found for ${pkgStripped}!`);
|
|
126
|
-
logger.error(`[registerPieElementsFromBundle] Bundle type: ${options.bundleType} (controllers required)`);
|
|
127
|
-
throw new Error(`No controller found for ${pkgStripped}. client-player.js bundles MUST include controllers!`);
|
|
128
|
-
}
|
|
129
|
-
else {
|
|
130
|
-
logger.debug(`[registerPieElementsFromBundle] ℹ️ No controller found for ${pkgStripped} - using server-processed models (player.js bundle)`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
123
|
{
|
|
134
|
-
// Register the element in our registry
|
|
124
|
+
// Register the element in our registry. A package without a
|
|
125
|
+
// controller, such as a legacy `@pie-element/protractor`, renders
|
|
126
|
+
// the model it is given under client-player.js too.
|
|
135
127
|
logger.debug(`[registerPieElementsFromBundle] Registering ${elName} in registry${elementData.controller
|
|
136
128
|
? " with controller"
|
|
137
|
-
: " (no controller
|
|
138
|
-
|
|
129
|
+
: " (no controller; its model is used as given)"}`);
|
|
130
|
+
writeRegistryEntry({
|
|
139
131
|
package: pkg,
|
|
140
132
|
status: Status.loading,
|
|
141
133
|
tagName: elementTagName,
|
|
142
134
|
controller: elementData.controller || null,
|
|
143
135
|
config: elementData.config,
|
|
144
136
|
bundleType: options.bundleType,
|
|
145
|
-
};
|
|
137
|
+
});
|
|
146
138
|
if (isCustomElementConstructor(elementData.Element)) {
|
|
147
139
|
defineCustomElementSafely(elementTagName, elementData.Element, `element tag in config.elements for ${String(pkg)}`);
|
|
148
140
|
// Initialize existing elements
|
|
@@ -155,13 +147,13 @@ const registerPieElementsFromBundle = (elementModule, config, session, registry,
|
|
|
155
147
|
session,
|
|
156
148
|
env: options.env,
|
|
157
149
|
eventListeners: options.eventListeners?.[elementTagName],
|
|
150
|
+
bundleType: options.bundleType,
|
|
158
151
|
});
|
|
159
152
|
});
|
|
160
|
-
|
|
161
|
-
registry[elementTagName] = {
|
|
153
|
+
writeRegistryEntry({
|
|
162
154
|
...registry[elementTagName],
|
|
163
155
|
status: Status.loaded,
|
|
164
|
-
};
|
|
156
|
+
});
|
|
165
157
|
promises.push(customElements.whenDefined(elementTagName).then(() => {
|
|
166
158
|
logger.debug("[registerPieElementsFromBundle] defined custom PIE element: %s", elementTagName);
|
|
167
159
|
}));
|
|
@@ -297,9 +289,8 @@ export const loadPieModule = async (config, session, opts = {}) => {
|
|
|
297
289
|
}
|
|
298
290
|
catch (error) {
|
|
299
291
|
// `registerPieElementsFromBundle` throws synchronously for a
|
|
300
|
-
// package missing from the bundle
|
|
301
|
-
//
|
|
302
|
-
// throw reaches the window instead of the caller.
|
|
292
|
+
// package missing from the bundle. Inside a DOM event handler
|
|
293
|
+
// that throw reaches the window instead of the caller.
|
|
303
294
|
fail(error instanceof Error ? error : new Error(String(error)));
|
|
304
295
|
}
|
|
305
296
|
});
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { ConfigEntity, Env } from "../types/index.js";
|
|
8
8
|
import type { EventListeners, PieElement } from "./types.js";
|
|
9
|
+
import { BundleType } from "./types.js";
|
|
9
10
|
/**
|
|
10
11
|
* Bind `element` to the model in `options.config` whose `id` matches it.
|
|
11
12
|
*
|
|
@@ -20,4 +21,6 @@ export declare const initializePieElement: (element: PieElement, options: {
|
|
|
20
21
|
session: any[];
|
|
21
22
|
env?: Env;
|
|
22
23
|
eventListeners?: EventListeners;
|
|
24
|
+
/** The delivering player's bundle type, see `findPieController`. */
|
|
25
|
+
bundleType?: BundleType;
|
|
23
26
|
}) => boolean;
|
|
@@ -4,7 +4,6 @@
|
|
|
4
4
|
* Split out of `initialization.ts` so the late-arrival observer in
|
|
5
5
|
* `element-observer.ts` can reuse it without importing the bundle loaders.
|
|
6
6
|
*/
|
|
7
|
-
import { wrapModelRichContent } from "../security/wrap-model-rich-content.js";
|
|
8
7
|
import { createPieLogger, isGlobalDebugEnabled } from "./logger.js";
|
|
9
8
|
import { pieRegistry } from "./registry.js";
|
|
10
9
|
import { findPieController } from "./scoring.js";
|
|
@@ -21,7 +20,7 @@ const logger = createPieLogger("pie-initialize-element", () => isGlobalDebugEnab
|
|
|
21
20
|
* separately — uses that to stop at the config the element belongs to.
|
|
22
21
|
*/
|
|
23
22
|
export const initializePieElement = (element, options) => {
|
|
24
|
-
const { config, session, env, eventListeners } = options;
|
|
23
|
+
const { config, session, env, eventListeners, bundleType } = options;
|
|
25
24
|
if (element.__pieInitialized) {
|
|
26
25
|
return true;
|
|
27
26
|
}
|
|
@@ -45,7 +44,7 @@ export const initializePieElement = (element, options) => {
|
|
|
45
44
|
element.__pieInitialized = true;
|
|
46
45
|
logger.debug(`[initializePieElement] Session set for ${tagName}#${element.id}:`, elementSession);
|
|
47
46
|
// Set model - use controller if available (client-player.js), or use server-processed model (player.js)
|
|
48
|
-
const controller = findPieController(tagName);
|
|
47
|
+
const controller = findPieController(tagName, bundleType);
|
|
49
48
|
if (!env) {
|
|
50
49
|
logger.error(`[initializePieElement] ❌ FATAL: No env provided for ${tagName}`);
|
|
51
50
|
throw new Error(`No env provided for ${tagName}. PIE elements require an env object with mode and role.`);
|
|
@@ -61,7 +60,7 @@ export const initializePieElement = (element, options) => {
|
|
|
61
60
|
role: env.role,
|
|
62
61
|
});
|
|
63
62
|
// Set model directly - server already processed it
|
|
64
|
-
element.model =
|
|
63
|
+
element.model = model;
|
|
65
64
|
}
|
|
66
65
|
else {
|
|
67
66
|
// Controller available - run client-side processing (client-player.js bundle)
|
|
@@ -37,7 +37,8 @@ export const SECTION_INSTRUMENTATION_EVENT_MAP = [
|
|
|
37
37
|
// instrumentation bridge forwards the same names to telemetry. The
|
|
38
38
|
// readiness alias mappings (`readiness-change`, `interaction-ready`,
|
|
39
39
|
// `ready`) and the `section-controller-ready` mapping were removed
|
|
40
|
-
// alongside their DOM-event surfaces.
|
|
40
|
+
// alongside their DOM-event surfaces. `session-changed` stays off the
|
|
41
|
+
// bridge for the reason the item map gives below.
|
|
41
42
|
{
|
|
42
43
|
sourceEventName: "pie-stage-change",
|
|
43
44
|
instrumentationEventName: "pie-section-stage-change",
|
|
@@ -46,14 +47,6 @@ export const SECTION_INSTRUMENTATION_EVENT_MAP = [
|
|
|
46
47
|
sourceEventName: "pie-loading-complete",
|
|
47
48
|
instrumentationEventName: "pie-section-loading-complete",
|
|
48
49
|
},
|
|
49
|
-
{
|
|
50
|
-
sourceEventName: "session-changed",
|
|
51
|
-
instrumentationEventName: "pie-section-session-changed",
|
|
52
|
-
},
|
|
53
|
-
{
|
|
54
|
-
sourceEventName: "composition-changed",
|
|
55
|
-
instrumentationEventName: "pie-section-composition-changed",
|
|
56
|
-
},
|
|
57
50
|
{
|
|
58
51
|
sourceEventName: "framework-error",
|
|
59
52
|
instrumentationEventName: "pie-section-framework-error",
|
|
@@ -1,6 +1,7 @@
|
|
|
1
|
+
import type { InstrumentationProvider } from "../instrumentation/types.js";
|
|
1
2
|
export declare function resolveInstrumentationProvider(args: {
|
|
2
3
|
runtimePlayer?: unknown;
|
|
3
4
|
player?: unknown;
|
|
4
5
|
debug?: boolean;
|
|
5
6
|
component?: string;
|
|
6
|
-
}):
|
|
7
|
+
}): InstrumentationProvider | undefined;
|
|
@@ -37,7 +37,6 @@ const logger = createPieLogger("instrumentation-provider-resolution", () => isGl
|
|
|
37
37
|
function getDefaultInstrumentationProvider() {
|
|
38
38
|
if (!defaultProvider) {
|
|
39
39
|
defaultProvider = new NewRelicInstrumentationProvider();
|
|
40
|
-
// New Relic provider sets readiness based on window.newrelic.
|
|
41
40
|
void defaultProvider.initialize();
|
|
42
41
|
}
|
|
43
42
|
return defaultProvider;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SessionCommitReason } from "./session-commit.js";
|
|
1
2
|
export type ItemSessionContainer = {
|
|
2
3
|
id: string;
|
|
3
4
|
data: unknown[];
|
|
@@ -8,7 +9,14 @@ export type NormalizedItemSessionChange = {
|
|
|
8
9
|
session: ItemSessionContainer | null;
|
|
9
10
|
intent: ItemSessionUpdateIntent;
|
|
10
11
|
component?: string;
|
|
12
|
+
/**
|
|
13
|
+
* The id of the element whose change this is: the detail's `elementId`, or
|
|
14
|
+
* the `id` of an element session record carried as `session`.
|
|
15
|
+
*/
|
|
16
|
+
elementId?: string;
|
|
11
17
|
complete?: boolean;
|
|
18
|
+
/** The detail's `sessionCommitReason`, when the change is a commit. */
|
|
19
|
+
sessionCommitReason?: SessionCommitReason;
|
|
12
20
|
};
|
|
13
21
|
export declare function normalizeItemSessionContainer(input: unknown, fallbackSessionId?: string): ItemSessionContainer;
|
|
14
22
|
export declare function hasResponseValue(value: unknown): boolean;
|
|
@@ -2,6 +2,7 @@ const DEFAULT_SESSION_ID = "";
|
|
|
2
2
|
const METADATA_ONLY_SESSION_KEYS = new Set([
|
|
3
3
|
"complete",
|
|
4
4
|
"component",
|
|
5
|
+
"elementId",
|
|
5
6
|
"timestamp",
|
|
6
7
|
"sourceRuntimeId",
|
|
7
8
|
]);
|
|
@@ -162,6 +163,18 @@ function getMetadataComplete(sessionDetail, candidate) {
|
|
|
162
163
|
return candidate.complete;
|
|
163
164
|
return undefined;
|
|
164
165
|
}
|
|
166
|
+
/**
|
|
167
|
+
* `elementRecord` is an element's own session record, whose `id` is the
|
|
168
|
+
* element's; an item session container's `id` is the item session's.
|
|
169
|
+
*/
|
|
170
|
+
function withElementId(sessionDetail, elementRecord) {
|
|
171
|
+
const elementId = typeof sessionDetail.elementId === "string" && sessionDetail.elementId
|
|
172
|
+
? sessionDetail.elementId
|
|
173
|
+
: typeof elementRecord?.id === "string" && elementRecord.id
|
|
174
|
+
? elementRecord.id
|
|
175
|
+
: undefined;
|
|
176
|
+
return elementId ? { elementId } : {};
|
|
177
|
+
}
|
|
165
178
|
function isElementIdentityOnlyPayload(candidate) {
|
|
166
179
|
const candidateKeys = Object.keys(candidate);
|
|
167
180
|
const hasIdentityKey = "id" in candidate || "element" in candidate;
|
|
@@ -211,6 +224,14 @@ export function mergeElementIntoSession(itemId, previousItemSession, entryId, en
|
|
|
211
224
|
};
|
|
212
225
|
}
|
|
213
226
|
export function normalizeItemSessionChange(args) {
|
|
227
|
+
const change = classifyItemSessionChange(args);
|
|
228
|
+
const reason = args.sessionDetail
|
|
229
|
+
?.sessionCommitReason;
|
|
230
|
+
return typeof reason === "string" && reason
|
|
231
|
+
? { ...change, sessionCommitReason: reason }
|
|
232
|
+
: change;
|
|
233
|
+
}
|
|
234
|
+
function classifyItemSessionChange(args) {
|
|
214
235
|
const sessionDetail = (args.sessionDetail || {});
|
|
215
236
|
const actualSession = sessionDetail &&
|
|
216
237
|
typeof sessionDetail === "object" &&
|
|
@@ -229,6 +250,7 @@ export function normalizeItemSessionChange(args) {
|
|
|
229
250
|
session: null,
|
|
230
251
|
intent: "metadata-only",
|
|
231
252
|
component: getMetadataComponent(sessionDetail),
|
|
253
|
+
...withElementId(sessionDetail),
|
|
232
254
|
complete: getMetadataComplete(sessionDetail),
|
|
233
255
|
};
|
|
234
256
|
}
|
|
@@ -241,11 +263,7 @@ export function normalizeItemSessionChange(args) {
|
|
|
241
263
|
const previousForMetadataComparison = alignPreviousSessionIdForRendererSnapshot(normalizedCandidate, previousNormalized);
|
|
242
264
|
const sessionDetailKeys = Object.keys(sessionDetail);
|
|
243
265
|
const metadataWithSessionOnly = sessionDetailKeys.length > 0 &&
|
|
244
|
-
sessionDetailKeys.every((key) => key === "session" ||
|
|
245
|
-
key === "complete" ||
|
|
246
|
-
key === "component" ||
|
|
247
|
-
key === "timestamp" ||
|
|
248
|
-
key === "sourceRuntimeId");
|
|
266
|
+
sessionDetailKeys.every((key) => key === "session" || METADATA_ONLY_SESSION_KEYS.has(key));
|
|
249
267
|
const hasMetadataBeyondSession = sessionDetailKeys.some((key) => key !== "session");
|
|
250
268
|
if (metadataWithSessionOnly &&
|
|
251
269
|
hasMetadataBeyondSession &&
|
|
@@ -256,6 +274,7 @@ export function normalizeItemSessionChange(args) {
|
|
|
256
274
|
session: null,
|
|
257
275
|
intent: "metadata-only",
|
|
258
276
|
component: getMetadataComponent(sessionDetail),
|
|
277
|
+
...withElementId(sessionDetail),
|
|
259
278
|
complete: getMetadataComplete(sessionDetail),
|
|
260
279
|
};
|
|
261
280
|
}
|
|
@@ -266,6 +285,7 @@ export function normalizeItemSessionChange(args) {
|
|
|
266
285
|
session: null,
|
|
267
286
|
intent: "metadata-only",
|
|
268
287
|
component: getMetadataComponent(sessionDetail),
|
|
288
|
+
...withElementId(sessionDetail),
|
|
269
289
|
complete: getMetadataComplete(sessionDetail),
|
|
270
290
|
};
|
|
271
291
|
}
|
|
@@ -274,21 +294,20 @@ export function normalizeItemSessionChange(args) {
|
|
|
274
294
|
session: normalizedCandidate,
|
|
275
295
|
intent: "replace-item-session",
|
|
276
296
|
component: getMetadataComponent(sessionDetail),
|
|
297
|
+
...withElementId(sessionDetail),
|
|
277
298
|
complete: getMetadataComplete(sessionDetail),
|
|
278
299
|
};
|
|
279
300
|
}
|
|
280
301
|
const candidateKeys = Object.keys(candidate);
|
|
281
302
|
const isMetadataOnlyPayload = candidateKeys.length > 0 &&
|
|
282
|
-
candidateKeys.every((key) => key
|
|
283
|
-
key === "component" ||
|
|
284
|
-
key === "timestamp" ||
|
|
285
|
-
key === "sourceRuntimeId");
|
|
303
|
+
candidateKeys.every((key) => METADATA_ONLY_SESSION_KEYS.has(key));
|
|
286
304
|
if (isMetadataOnlyPayload) {
|
|
287
305
|
return {
|
|
288
306
|
itemId: safeItemId,
|
|
289
307
|
session: null,
|
|
290
308
|
intent: "metadata-only",
|
|
291
309
|
component: getMetadataComponent(sessionDetail, candidate),
|
|
310
|
+
...withElementId(sessionDetail),
|
|
292
311
|
complete: getMetadataComplete(sessionDetail, candidate),
|
|
293
312
|
};
|
|
294
313
|
}
|
|
@@ -298,6 +317,7 @@ export function normalizeItemSessionChange(args) {
|
|
|
298
317
|
session: null,
|
|
299
318
|
intent: "metadata-only",
|
|
300
319
|
component: getMetadataComponent(sessionDetail, candidate),
|
|
320
|
+
...withElementId(sessionDetail, candidate),
|
|
301
321
|
complete: getMetadataComplete(sessionDetail, candidate),
|
|
302
322
|
};
|
|
303
323
|
}
|
|
@@ -309,6 +329,7 @@ export function normalizeItemSessionChange(args) {
|
|
|
309
329
|
session: merged,
|
|
310
330
|
intent: "merge-element-session",
|
|
311
331
|
component,
|
|
332
|
+
...withElementId(sessionDetail, candidate),
|
|
312
333
|
complete: getMetadataComplete(sessionDetail, candidate),
|
|
313
334
|
};
|
|
314
335
|
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Math in an item's own markup: what the item and passage markup carry outside
|
|
3
|
+
* every PIE element.
|
|
4
|
+
*
|
|
5
|
+
* Each element typesets its own subtree, so the player hands its renderer only
|
|
6
|
+
* the markup around them. A renderer finds math below the element it is given,
|
|
7
|
+
* so no root holds a PIE element: the markup splits into the elements that hold
|
|
8
|
+
* none, and math sharing a parent with one is wrapped in a span of its own.
|
|
9
|
+
*/
|
|
10
|
+
import type { MathRenderer } from "./math-rendering.js";
|
|
11
|
+
/**
|
|
12
|
+
* The parts of `container` holding math and no PIE element, each one a root to
|
|
13
|
+
* typeset. `pieTags` are the tag names of the PIE elements its markup places.
|
|
14
|
+
*/
|
|
15
|
+
export declare function collectMarkupMathRoots(container: HTMLElement, pieTags: ReadonlySet<string>): HTMLElement[];
|
|
16
|
+
/**
|
|
17
|
+
* Typesets the math in `containers` outside every PIE element with `render`,
|
|
18
|
+
* one root at a time.
|
|
19
|
+
*/
|
|
20
|
+
export declare function typesetMarkupMath(containers: Iterable<HTMLElement>, pieTags: ReadonlySet<string>, render: MathRenderer): Promise<void>;
|
|
@@ -0,0 +1,118 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Math in an item's own markup: what the item and passage markup carry outside
|
|
3
|
+
* every PIE element.
|
|
4
|
+
*
|
|
5
|
+
* Each element typesets its own subtree, so the player hands its renderer only
|
|
6
|
+
* the markup around them. A renderer finds math below the element it is given,
|
|
7
|
+
* so no root holds a PIE element: the markup splits into the elements that hold
|
|
8
|
+
* none, and math sharing a parent with one is wrapped in a span of its own.
|
|
9
|
+
*/
|
|
10
|
+
/** TeX as `@pie-element/shared-math-rendering-mathjax` detects it. */
|
|
11
|
+
const TEX = /\\\(|\\\[|\$\$|\\\$|\\begin\{|\\(?:eq)?ref\{/;
|
|
12
|
+
/** The same, with `$…$` inline math turned on by the legacy opt-in. */
|
|
13
|
+
const TEX_WITH_SINGLE_DOLLAR = /\\\(|\\\[|\$|\\begin\{|\\(?:eq)?ref\{/;
|
|
14
|
+
/** Elements whose content MathJax leaves alone, as both PIE configurations list them. */
|
|
15
|
+
const SKIPPED_ELEMENTS = new Set([
|
|
16
|
+
"script",
|
|
17
|
+
"noscript",
|
|
18
|
+
"style",
|
|
19
|
+
"textarea",
|
|
20
|
+
"pre",
|
|
21
|
+
"code",
|
|
22
|
+
"annotation",
|
|
23
|
+
"annotation-xml",
|
|
24
|
+
"mjx-container",
|
|
25
|
+
"mjx-assistive-mml",
|
|
26
|
+
]);
|
|
27
|
+
/** The class an author marks content with to keep MathJax out of it. */
|
|
28
|
+
const IGNORED_CLASS = /(?:^|\s)mathjax_ignore(?:\s|$)/;
|
|
29
|
+
const HTML_NAMESPACE = "http://www.w3.org/1999/xhtml";
|
|
30
|
+
function texPattern() {
|
|
31
|
+
const legacy = globalThis["@pie-lib/math-rendering@2"];
|
|
32
|
+
return legacy?.opts?.useSingleDollar ? TEX_WITH_SINGLE_DOLLAR : TEX;
|
|
33
|
+
}
|
|
34
|
+
function isSkipped(element) {
|
|
35
|
+
return (SKIPPED_ELEMENTS.has(element.localName) ||
|
|
36
|
+
IGNORED_CLASS.test(element.getAttribute("class") ?? ""));
|
|
37
|
+
}
|
|
38
|
+
/** Math a renderer would only find on `element` itself, never below it. */
|
|
39
|
+
function holdsOwnMath(element) {
|
|
40
|
+
return (element.localName === "math" ||
|
|
41
|
+
(element.hasAttribute("data-latex") &&
|
|
42
|
+
!element.hasAttribute("data-math-handled")));
|
|
43
|
+
}
|
|
44
|
+
function holdsMath(element, tex) {
|
|
45
|
+
if (holdsOwnMath(element) ||
|
|
46
|
+
element.querySelector("[data-latex]:not([data-math-handled])")) {
|
|
47
|
+
return true;
|
|
48
|
+
}
|
|
49
|
+
for (const math of element.getElementsByTagName("math")) {
|
|
50
|
+
if (!math.closest("mjx-assistive-mml"))
|
|
51
|
+
return true;
|
|
52
|
+
}
|
|
53
|
+
return tex.test(element.textContent ?? "");
|
|
54
|
+
}
|
|
55
|
+
function wrapInSpan(node) {
|
|
56
|
+
const span = (node.ownerDocument ?? document).createElement("span");
|
|
57
|
+
node.replaceWith(span);
|
|
58
|
+
span.append(node);
|
|
59
|
+
return span;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* The parts of `container` holding math and no PIE element, each one a root to
|
|
63
|
+
* typeset. `pieTags` are the tag names of the PIE elements its markup places.
|
|
64
|
+
*/
|
|
65
|
+
export function collectMarkupMathRoots(container, pieTags) {
|
|
66
|
+
const tex = texPattern();
|
|
67
|
+
const holdsPie = new Set();
|
|
68
|
+
for (const tag of pieTags) {
|
|
69
|
+
for (const element of container.getElementsByTagName(tag)) {
|
|
70
|
+
for (let ancestor = element.parentElement; ancestor && !holdsPie.has(ancestor); ancestor = ancestor.parentElement) {
|
|
71
|
+
holdsPie.add(ancestor);
|
|
72
|
+
if (ancestor === container)
|
|
73
|
+
break;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
if (!holdsPie.has(container)) {
|
|
78
|
+
return holdsMath(container, tex) ? [container] : [];
|
|
79
|
+
}
|
|
80
|
+
const roots = [];
|
|
81
|
+
const visit = (parent) => {
|
|
82
|
+
for (const child of [...parent.childNodes]) {
|
|
83
|
+
if (child.nodeType === Node.TEXT_NODE) {
|
|
84
|
+
if (tex.test(child.data))
|
|
85
|
+
roots.push(wrapInSpan(child));
|
|
86
|
+
continue;
|
|
87
|
+
}
|
|
88
|
+
if (child.nodeType !== Node.ELEMENT_NODE)
|
|
89
|
+
continue;
|
|
90
|
+
const element = child;
|
|
91
|
+
if (pieTags.has(element.localName) || isSkipped(element))
|
|
92
|
+
continue;
|
|
93
|
+
if (holdsPie.has(element)) {
|
|
94
|
+
visit(element);
|
|
95
|
+
}
|
|
96
|
+
else if (holdsMath(element, tex)) {
|
|
97
|
+
if (holdsOwnMath(element)) {
|
|
98
|
+
roots.push(wrapInSpan(element));
|
|
99
|
+
}
|
|
100
|
+
else if (element.namespaceURI === HTML_NAMESPACE) {
|
|
101
|
+
roots.push(element);
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
};
|
|
106
|
+
visit(container);
|
|
107
|
+
return roots;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Typesets the math in `containers` outside every PIE element with `render`,
|
|
111
|
+
* one root at a time.
|
|
112
|
+
*/
|
|
113
|
+
export async function typesetMarkupMath(containers, pieTags, render) {
|
|
114
|
+
const roots = [...containers].flatMap((container) => collectMarkupMathRoots(container, pieTags));
|
|
115
|
+
for (const root of roots) {
|
|
116
|
+
await render(root);
|
|
117
|
+
}
|
|
118
|
+
}
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
* PIE elements expect @pie-lib/math-rendering to be available on window.
|
|
5
5
|
* This module ensures required globals are populated from the upstream
|
|
6
6
|
* @pie-lib/math-rendering-module package, and also supports overriding with
|
|
7
|
-
* a custom renderer object
|
|
7
|
+
* a custom renderer object through setMathRenderer().
|
|
8
8
|
*/
|
|
9
9
|
export type MathRenderer = (element: HTMLElement) => void | Promise<void>;
|
|
10
10
|
export interface MathRenderingAPI {
|
|
@@ -14,32 +14,30 @@ export interface MathRenderingAPI {
|
|
|
14
14
|
mmlToLatex?: (mathml: string) => string;
|
|
15
15
|
}
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
17
|
+
* The page's renderer: the one `initializeMathRendering` installed, or a
|
|
18
|
+
* host's. IIFE elements render with it, and so do ESM elements when the page
|
|
19
|
+
* has one.
|
|
20
|
+
*/
|
|
21
|
+
export declare const getMathRenderer: () => MathRenderingAPI | null;
|
|
22
|
+
/**
|
|
23
|
+
* Initialize math rendering, defaulting to MathJax.
|
|
20
24
|
* For custom renderers, use setMathRenderer() before calling this function.
|
|
21
25
|
*
|
|
22
26
|
* Sets TWO window globals that PIE elements expect:
|
|
23
27
|
* - window["@pie-lib/math-rendering"] (standard key)
|
|
24
28
|
* - window["_dll_pie_lib__math_rendering"] (SystemJS/DLL key for IIFE bundles)
|
|
25
29
|
*
|
|
26
|
-
* @param customRenderer - Optional custom renderer to use instead of default MathJax
|
|
27
|
-
*
|
|
28
30
|
* @example
|
|
29
31
|
* ```typescript
|
|
30
32
|
* // Default MathJax
|
|
31
33
|
* await initializeMathRendering();
|
|
32
34
|
*
|
|
33
|
-
* // Custom renderer
|
|
34
|
-
* const customRenderer = await createCustomRenderer();
|
|
35
|
-
* await initializeMathRendering(customRenderer);
|
|
36
|
-
*
|
|
37
|
-
* // Or use setMathRenderer first
|
|
35
|
+
* // Custom renderer: install it first
|
|
38
36
|
* setMathRenderer(katexRenderer);
|
|
39
37
|
* await initializeMathRendering();
|
|
40
38
|
* ```
|
|
41
39
|
*/
|
|
42
|
-
export declare function initializeMathRendering(
|
|
40
|
+
export declare function initializeMathRendering(): Promise<void>;
|
|
43
41
|
/**
|
|
44
42
|
* Set custom math renderer programmatically
|
|
45
43
|
*
|
|
@@ -60,12 +58,3 @@ export declare function initializeMathRendering(customRenderer?: MathRenderingAP
|
|
|
60
58
|
* ```
|
|
61
59
|
*/
|
|
62
60
|
export declare function setMathRenderer(renderer: MathRenderingAPI): void;
|
|
63
|
-
/**
|
|
64
|
-
* Render math in the given element
|
|
65
|
-
*
|
|
66
|
-
* Convenience wrapper that calls the active renderer's renderMath() function.
|
|
67
|
-
* Typically called after PIE elements are rendered.
|
|
68
|
-
*
|
|
69
|
-
* @param element - The element to render math within
|
|
70
|
-
*/
|
|
71
|
-
export declare function renderMath(element: HTMLElement): void;
|
|
@@ -4,15 +4,23 @@
|
|
|
4
4
|
* PIE elements expect @pie-lib/math-rendering to be available on window.
|
|
5
5
|
* This module ensures required globals are populated from the upstream
|
|
6
6
|
* @pie-lib/math-rendering-module package, and also supports overriding with
|
|
7
|
-
* a custom renderer object
|
|
7
|
+
* a custom renderer object through setMathRenderer().
|
|
8
8
|
*/
|
|
9
9
|
/// <reference path="../shims.d.ts" />
|
|
10
10
|
import { createPieLogger, isGlobalDebugEnabled } from "./logger.js";
|
|
11
11
|
const GLOBAL_KEY = "@pie-lib/math-rendering";
|
|
12
12
|
const GLOBAL_DLL_KEY = "_dll_pie_lib__math_rendering";
|
|
13
|
+
// Every copy of @pie-lib/math-rendering on a page typesets through the MathJax
|
|
14
|
+
// instance kept here, and the first copy to typeset creates it.
|
|
15
|
+
const SHARED_INSTANCE_KEY = "@pie-lib/math-rendering@2";
|
|
13
16
|
let initPromise = null;
|
|
14
17
|
const logger = createPieLogger("math-rendering", () => isGlobalDebugEnabled());
|
|
15
|
-
|
|
18
|
+
/**
|
|
19
|
+
* The page's renderer: the one `initializeMathRendering` installed, or a
|
|
20
|
+
* host's. IIFE elements render with it, and so do ESM elements when the page
|
|
21
|
+
* has one.
|
|
22
|
+
*/
|
|
23
|
+
export const getMathRenderer = () => {
|
|
16
24
|
if (typeof window === "undefined") {
|
|
17
25
|
return null;
|
|
18
26
|
}
|
|
@@ -26,43 +34,55 @@ const setWindowRenderer = (renderer) => {
|
|
|
26
34
|
window[GLOBAL_DLL_KEY] = renderer;
|
|
27
35
|
};
|
|
28
36
|
/**
|
|
29
|
-
*
|
|
37
|
+
* Creates the shared MathJax instance from `renderer`, unless the page already
|
|
38
|
+
* holds one.
|
|
30
39
|
*
|
|
31
|
-
*
|
|
40
|
+
* Legacy element bundles carry their own copy of math-rendering. A copy that
|
|
41
|
+
* predates the assistive-MathML guard, such as the one in multiple-choice
|
|
42
|
+
* 9.9.1, re-typesets the hidden `<math>` inside `mjx-assistive-mml` on every
|
|
43
|
+
* pass, so if it creates the instance, each re-render nests the expression
|
|
44
|
+
* again for every element on the page. Typesetting a detached element makes the
|
|
45
|
+
* guarded module create it first. Failure leaves the instance to the first
|
|
46
|
+
* element, as before, so it is logged and never fails initialization.
|
|
47
|
+
*/
|
|
48
|
+
const createSharedInstance = (renderer) => {
|
|
49
|
+
if (window[SHARED_INSTANCE_KEY]?.instance)
|
|
50
|
+
return;
|
|
51
|
+
if (typeof document === "undefined" || !document.body)
|
|
52
|
+
return;
|
|
53
|
+
const warn = (error) => logger.warn("Could not create the shared MathJax instance:", error);
|
|
54
|
+
try {
|
|
55
|
+
Promise.resolve(renderer.renderMath(document.createElement("div"))).catch(warn);
|
|
56
|
+
}
|
|
57
|
+
catch (error) {
|
|
58
|
+
warn(error);
|
|
59
|
+
}
|
|
60
|
+
};
|
|
61
|
+
/**
|
|
62
|
+
* Initialize math rendering, defaulting to MathJax.
|
|
32
63
|
* For custom renderers, use setMathRenderer() before calling this function.
|
|
33
64
|
*
|
|
34
65
|
* Sets TWO window globals that PIE elements expect:
|
|
35
66
|
* - window["@pie-lib/math-rendering"] (standard key)
|
|
36
67
|
* - window["_dll_pie_lib__math_rendering"] (SystemJS/DLL key for IIFE bundles)
|
|
37
68
|
*
|
|
38
|
-
* @param customRenderer - Optional custom renderer to use instead of default MathJax
|
|
39
|
-
*
|
|
40
69
|
* @example
|
|
41
70
|
* ```typescript
|
|
42
71
|
* // Default MathJax
|
|
43
72
|
* await initializeMathRendering();
|
|
44
73
|
*
|
|
45
|
-
* // Custom renderer
|
|
46
|
-
* const customRenderer = await createCustomRenderer();
|
|
47
|
-
* await initializeMathRendering(customRenderer);
|
|
48
|
-
*
|
|
49
|
-
* // Or use setMathRenderer first
|
|
74
|
+
* // Custom renderer: install it first
|
|
50
75
|
* setMathRenderer(katexRenderer);
|
|
51
76
|
* await initializeMathRendering();
|
|
52
77
|
* ```
|
|
53
78
|
*/
|
|
54
|
-
export async function initializeMathRendering(
|
|
79
|
+
export async function initializeMathRendering() {
|
|
55
80
|
// Only run in browser
|
|
56
81
|
if (typeof window === "undefined") {
|
|
57
82
|
return;
|
|
58
83
|
}
|
|
59
|
-
// Explicit override always wins.
|
|
60
|
-
if (customRenderer) {
|
|
61
|
-
setWindowRenderer(customRenderer);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
84
|
// Already initialized - skip.
|
|
65
|
-
if (
|
|
85
|
+
if (getMathRenderer()) {
|
|
66
86
|
return;
|
|
67
87
|
}
|
|
68
88
|
if (initPromise) {
|
|
@@ -71,11 +91,20 @@ export async function initializeMathRendering(customRenderer) {
|
|
|
71
91
|
}
|
|
72
92
|
initPromise = (async () => {
|
|
73
93
|
try {
|
|
74
|
-
|
|
94
|
+
// The package has no exports map, so the file is named in full for
|
|
95
|
+
// resolvers that do not complete a directory, such as webpack's
|
|
96
|
+
// fully-specified ESM resolution.
|
|
97
|
+
const mathRenderingModule = await import("@pie-lib/math-rendering-module/module/index.js");
|
|
98
|
+
// The player builds defer the module's evaluation to this call (see
|
|
99
|
+
// math-rendering-module-deferral.mjs); unbundled, it evaluated on import.
|
|
100
|
+
const renderer = mathRenderingModule.evaluateMathRenderingModule
|
|
101
|
+
? mathRenderingModule.evaluateMathRenderingModule()
|
|
102
|
+
: mathRenderingModule._dll_pie_lib__math_rendering;
|
|
75
103
|
// A host may install its renderer while the default module is in flight.
|
|
76
104
|
// The explicit renderer remains authoritative when that happens.
|
|
77
|
-
if (!
|
|
78
|
-
setWindowRenderer(
|
|
105
|
+
if (!getMathRenderer()) {
|
|
106
|
+
setWindowRenderer(renderer);
|
|
107
|
+
createSharedInstance(renderer);
|
|
79
108
|
logger.debug("Math rendering module initialized (both globals set)");
|
|
80
109
|
}
|
|
81
110
|
}
|
|
@@ -113,17 +142,3 @@ export async function initializeMathRendering(customRenderer) {
|
|
|
113
142
|
export function setMathRenderer(renderer) {
|
|
114
143
|
setWindowRenderer(renderer);
|
|
115
144
|
}
|
|
116
|
-
/**
|
|
117
|
-
* Render math in the given element
|
|
118
|
-
*
|
|
119
|
-
* Convenience wrapper that calls the active renderer's renderMath() function.
|
|
120
|
-
* Typically called after PIE elements are rendered.
|
|
121
|
-
*
|
|
122
|
-
* @param element - The element to render math within
|
|
123
|
-
*/
|
|
124
|
-
export function renderMath(element) {
|
|
125
|
-
const renderer = getWindowRenderer();
|
|
126
|
-
if (renderer && typeof renderer.renderMath === "function") {
|
|
127
|
-
renderer.renderMath(element);
|
|
128
|
-
}
|
|
129
|
-
}
|