@citolab/qti-components 7.22.1 → 7.24.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/cdn/index.global.js +1 -1
- package/cdn/index.js +2569 -2901
- package/custom-elements.json +91 -208
- package/dist/base.js +1 -1
- package/dist/{chunk-NUNAE73U.js → chunk-2X7747Q4.js} +1111 -1364
- package/dist/chunk-2X7747Q4.js.map +1 -0
- package/dist/{chunk-ULXR5TWK.js → chunk-4A7ZMQKU.js} +3 -3
- package/dist/{chunk-SVCFKO4U.js → chunk-53QVHUUQ.js} +2 -2
- package/dist/{chunk-EGLWHNOX.js → chunk-BHB6PYJG.js} +4 -4
- package/dist/{chunk-EGLWHNOX.js.map → chunk-BHB6PYJG.js.map} +1 -1
- package/dist/{chunk-MF25NAZ4.js → chunk-ES2FREAP.js} +4 -4
- package/dist/{chunk-MF25NAZ4.js.map → chunk-ES2FREAP.js.map} +1 -1
- package/dist/{chunk-YAGFD5RQ.js → chunk-FEO7D54Z.js} +3 -3
- package/dist/chunk-FEO7D54Z.js.map +1 -0
- package/dist/{chunk-HCVDQUP7.js → chunk-IKBPPSNQ.js} +1516 -1696
- package/dist/chunk-IKBPPSNQ.js.map +1 -0
- package/dist/{chunk-QZLVYJDX.js → chunk-VQAG7NSK.js} +2 -2
- package/dist/elements.js +3 -3
- package/dist/index.js +8 -8
- package/dist/interactions.d.ts +8 -23
- package/dist/interactions.js +3 -3
- package/dist/item.css +1513 -1693
- package/dist/item.js +3 -3
- package/dist/processing.js +2 -2
- package/dist/qti-components-jsx.d.ts +6 -10
- package/dist/test.js +5 -5
- package/package.json +12 -11
- package/dist/chunk-HCVDQUP7.js.map +0 -1
- package/dist/chunk-NUNAE73U.js.map +0 -1
- package/dist/chunk-YAGFD5RQ.js.map +0 -1
- /package/dist/{chunk-ULXR5TWK.js.map → chunk-4A7ZMQKU.js.map} +0 -0
- /package/dist/{chunk-SVCFKO4U.js.map → chunk-53QVHUUQ.js.map} +0 -0
- /package/dist/{chunk-QZLVYJDX.js.map → chunk-VQAG7NSK.js.map} +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
o as o2
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-VQAG7NSK.js";
|
|
4
4
|
import {
|
|
5
5
|
E,
|
|
6
6
|
Interaction,
|
|
@@ -30,7 +30,7 @@ import {
|
|
|
30
30
|
w,
|
|
31
31
|
watch,
|
|
32
32
|
x
|
|
33
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-BHB6PYJG.js";
|
|
34
34
|
import {
|
|
35
35
|
__decorateClass
|
|
36
36
|
} from "./chunk-QXBXORM3.js";
|
|
@@ -363,9 +363,9 @@ var DragDropInteractionMixin = (superClass, draggablesSelector, droppablesSelect
|
|
|
363
363
|
if (!referenceContainer || referenceContainer.clientWidth == 0) {
|
|
364
364
|
return this.MAX_DRAGGABLE_WIDTH;
|
|
365
365
|
}
|
|
366
|
-
const
|
|
367
|
-
const paddingLeft = parseFloat(
|
|
368
|
-
const paddingRight = parseFloat(
|
|
366
|
+
const styles3 = window.getComputedStyle(referenceContainer);
|
|
367
|
+
const paddingLeft = parseFloat(styles3.paddingLeft);
|
|
368
|
+
const paddingRight = parseFloat(styles3.paddingRight);
|
|
369
369
|
return Math.min(this.MAX_DRAGGABLE_WIDTH, referenceContainer.clientWidth - paddingLeft - paddingRight);
|
|
370
370
|
}
|
|
371
371
|
async measureIntrinsicSize(el) {
|
|
@@ -1224,7 +1224,7 @@ var ChoicesMixin = (superClass, selector) => {
|
|
|
1224
1224
|
this._setInputType(choice);
|
|
1225
1225
|
});
|
|
1226
1226
|
}
|
|
1227
|
-
_setInputType(choiceElement) {
|
|
1227
|
+
async _setInputType(choiceElement) {
|
|
1228
1228
|
this._internals.role = this.maxChoices === 1 ? "radiogroup" : null;
|
|
1229
1229
|
if (choiceElement.internals) {
|
|
1230
1230
|
const role = this.maxChoices === 1 ? "radio" : "checkbox";
|
|
@@ -1307,7 +1307,7 @@ var ChoicesMixin = (superClass, selector) => {
|
|
|
1307
1307
|
n({ type: Number, attribute: "max-choices" })
|
|
1308
1308
|
], ChoicesMixinElement.prototype, "maxChoices", 2);
|
|
1309
1309
|
__decorateClass([
|
|
1310
|
-
watch("maxChoices"
|
|
1310
|
+
watch("maxChoices")
|
|
1311
1311
|
], ChoicesMixinElement.prototype, "_handleMaxChoicesChange", 1);
|
|
1312
1312
|
__decorateClass([
|
|
1313
1313
|
watch("disabled", { waitUntilFirstUpdate: true })
|
|
@@ -2992,24 +2992,80 @@ var e3 = class extends i3 {
|
|
|
2992
2992
|
e3.directiveName = "unsafeHTML", e3.resultType = 1;
|
|
2993
2993
|
var o3 = e2(e3);
|
|
2994
2994
|
|
|
2995
|
+
// ../qti-interactions/src/components/qti-inline-choice-interaction/qti-inline-choice-interaction.styles.ts
|
|
2996
|
+
var styles = i`
|
|
2997
|
+
:host {
|
|
2998
|
+
display: inline-block;
|
|
2999
|
+
vertical-align: baseline;
|
|
3000
|
+
position: relative;
|
|
3001
|
+
}
|
|
3002
|
+
|
|
3003
|
+
button[part='trigger'] {
|
|
3004
|
+
anchor-name: --qti-inline-choice-trigger;
|
|
3005
|
+
}
|
|
3006
|
+
|
|
3007
|
+
[part='value'] {
|
|
3008
|
+
display: inline-flex;
|
|
3009
|
+
align-items: center;
|
|
3010
|
+
gap: 0.5rem;
|
|
3011
|
+
min-width: 0;
|
|
3012
|
+
}
|
|
3013
|
+
|
|
3014
|
+
[part~='dropdown-icon'] {
|
|
3015
|
+
line-height: 1;
|
|
3016
|
+
}
|
|
3017
|
+
|
|
3018
|
+
[part='menu'] {
|
|
3019
|
+
position-anchor: --qti-inline-choice-trigger;
|
|
3020
|
+
inset: auto;
|
|
3021
|
+
margin: 0;
|
|
3022
|
+
z-index: 1000;
|
|
3023
|
+
top: calc(anchor(bottom) + 4px);
|
|
3024
|
+
left: anchor(left);
|
|
3025
|
+
min-width: anchor-size(width);
|
|
3026
|
+
max-width: min(90vw, 36rem);
|
|
3027
|
+
max-height: min(40vh, 20rem);
|
|
3028
|
+
position-try-fallbacks: flip-block, flip-inline;
|
|
3029
|
+
}
|
|
3030
|
+
|
|
3031
|
+
button[part~='option'] {
|
|
3032
|
+
width: 100%;
|
|
3033
|
+
}
|
|
3034
|
+
|
|
3035
|
+
[part='option-content'] {
|
|
3036
|
+
display: flex;
|
|
3037
|
+
align-items: center;
|
|
3038
|
+
gap: 0.5rem;
|
|
3039
|
+
flex-wrap: nowrap;
|
|
3040
|
+
white-space: nowrap;
|
|
3041
|
+
overflow: hidden;
|
|
3042
|
+
text-overflow: ellipsis;
|
|
3043
|
+
min-width: 0;
|
|
3044
|
+
}
|
|
3045
|
+
|
|
3046
|
+
button[part~='option'] img,
|
|
3047
|
+
button[part='trigger'] img,
|
|
3048
|
+
[part='menu'] img {
|
|
3049
|
+
display: inline-block;
|
|
3050
|
+
vertical-align: middle;
|
|
3051
|
+
}
|
|
3052
|
+
`;
|
|
3053
|
+
var qti_inline_choice_interaction_styles_default = styles;
|
|
3054
|
+
|
|
2995
3055
|
// ../qti-interactions/src/components/qti-inline-choice-interaction/qti-inline-choice-interaction.ts
|
|
2996
|
-
var
|
|
3056
|
+
var inlineChoiceMenuCounter = 0;
|
|
3057
|
+
var QtiInlineChoiceInteraction = class extends Interaction {
|
|
2997
3058
|
constructor() {
|
|
2998
|
-
super(
|
|
3059
|
+
super();
|
|
2999
3060
|
this.options = [];
|
|
3000
3061
|
this.correctOption = "";
|
|
3001
3062
|
this._dropdownOpen = false;
|
|
3002
|
-
this.
|
|
3003
|
-
this.
|
|
3004
|
-
this
|
|
3005
|
-
|
|
3006
|
-
|
|
3007
|
-
|
|
3008
|
-
this.#selectValue(selectedOptionValue);
|
|
3009
|
-
};
|
|
3010
|
-
this.#onToggleCustomDropdown = () => {
|
|
3011
|
-
if (this.disabled || this.readonly) return;
|
|
3012
|
-
this.#setDropdownOpen(!this._dropdownOpen);
|
|
3063
|
+
this._slotObserver = null;
|
|
3064
|
+
this._menuId = `qti-inline-choice-menu-${inlineChoiceMenuCounter++}`;
|
|
3065
|
+
this.#onTriggerClick = (event) => {
|
|
3066
|
+
if (this.disabled || this.readonly) {
|
|
3067
|
+
event.preventDefault();
|
|
3068
|
+
}
|
|
3013
3069
|
};
|
|
3014
3070
|
this.#onCustomTriggerKeyDown = (event) => {
|
|
3015
3071
|
if (this.disabled || this.readonly) return;
|
|
@@ -3018,6 +3074,13 @@ var _QtiInlineChoiceInteraction = class _QtiInlineChoiceInteraction extends Inte
|
|
|
3018
3074
|
this.#setDropdownOpen(true);
|
|
3019
3075
|
}
|
|
3020
3076
|
};
|
|
3077
|
+
this.#onMenuToggle = (event) => {
|
|
3078
|
+
const toggleEvent = event;
|
|
3079
|
+
const open = toggleEvent.newState === "open";
|
|
3080
|
+
if (this._dropdownOpen !== open) {
|
|
3081
|
+
this._dropdownOpen = open;
|
|
3082
|
+
}
|
|
3083
|
+
};
|
|
3021
3084
|
this.#onCustomMenuKeyDown = (event) => {
|
|
3022
3085
|
if (!this._dropdownOpen) return;
|
|
3023
3086
|
if (event.key === "Escape") {
|
|
@@ -3026,417 +3089,125 @@ var _QtiInlineChoiceInteraction = class _QtiInlineChoiceInteraction extends Inte
|
|
|
3026
3089
|
this.#focusTrigger();
|
|
3027
3090
|
return;
|
|
3028
3091
|
}
|
|
3029
|
-
const
|
|
3030
|
-
const
|
|
3031
|
-
const
|
|
3092
|
+
const optionElements = this.#allMenuOptions();
|
|
3093
|
+
const shadowActive = this.renderRoot.activeElement;
|
|
3094
|
+
const deepActive = this.#getDeepActiveElement();
|
|
3095
|
+
const active = shadowActive || (deepActive instanceof HTMLElement && this.#isElementInsideInteraction(deepActive) ? deepActive : null);
|
|
3096
|
+
const activeIndex = optionElements.findIndex((el) => el === active);
|
|
3032
3097
|
if (event.key === "ArrowDown") {
|
|
3033
3098
|
event.preventDefault();
|
|
3034
|
-
|
|
3099
|
+
optionElements[Math.min(optionElements.length - 1, Math.max(0, activeIndex + 1))]?.focus();
|
|
3035
3100
|
}
|
|
3036
3101
|
if (event.key === "ArrowUp") {
|
|
3037
3102
|
event.preventDefault();
|
|
3038
|
-
|
|
3103
|
+
optionElements[Math.max(0, activeIndex - 1)]?.focus();
|
|
3104
|
+
}
|
|
3105
|
+
if (event.key === "Enter" || event.key === " ") {
|
|
3106
|
+
if (!active) return;
|
|
3107
|
+
event.preventDefault();
|
|
3108
|
+
if (active instanceof HTMLButtonElement) {
|
|
3109
|
+
active.click();
|
|
3110
|
+
} else {
|
|
3111
|
+
const value = active.getAttribute("identifier") ?? "";
|
|
3112
|
+
this.#selectValue(value);
|
|
3113
|
+
}
|
|
3039
3114
|
}
|
|
3040
3115
|
};
|
|
3041
|
-
this.#
|
|
3042
|
-
|
|
3043
|
-
const path = event.composedPath?.();
|
|
3044
|
-
if (path && path.includes(this)) return;
|
|
3045
|
-
this.#setDropdownOpen(false);
|
|
3116
|
+
this.#onChoicesSlotChange = () => {
|
|
3117
|
+
this.#updateOptions();
|
|
3046
3118
|
};
|
|
3047
|
-
this.#
|
|
3048
|
-
if (
|
|
3049
|
-
|
|
3050
|
-
|
|
3051
|
-
this.#
|
|
3052
|
-
this.#focusTrigger();
|
|
3119
|
+
this.#onSlottedChoiceClick = (event) => {
|
|
3120
|
+
if (this.disabled || this.readonly) return;
|
|
3121
|
+
const target = event.currentTarget;
|
|
3122
|
+
const value = target.getAttribute("identifier") ?? "";
|
|
3123
|
+
this.#selectValue(value);
|
|
3053
3124
|
};
|
|
3125
|
+
this.internals.role = "listbox";
|
|
3054
3126
|
}
|
|
3055
3127
|
get isInline() {
|
|
3056
3128
|
return true;
|
|
3057
3129
|
}
|
|
3058
|
-
static {
|
|
3059
|
-
this._supportsCustomizableSelectCache = null;
|
|
3060
|
-
}
|
|
3061
3130
|
static get styles() {
|
|
3062
|
-
return [
|
|
3063
|
-
i`
|
|
3064
|
-
:host {
|
|
3065
|
-
display: inline-block;
|
|
3066
|
-
vertical-align: baseline;
|
|
3067
|
-
position: relative;
|
|
3068
|
-
}
|
|
3069
|
-
|
|
3070
|
-
/* --- Progressive enhancement: Customizable select (MDN / WHATWG) --- */
|
|
3071
|
-
select[part='select'] {
|
|
3072
|
-
font: inherit;
|
|
3073
|
-
color: inherit;
|
|
3074
|
-
background-color: var(--qti-bg, white);
|
|
3075
|
-
border: var(--qti-border-thickness, 2px) var(--qti-border-style, solid) var(--qti-border-color, #c6cad0);
|
|
3076
|
-
border-radius: var(--qti-border-radius, 0.3rem);
|
|
3077
|
-
padding: 0.25rem 0.75rem;
|
|
3078
|
-
min-width: var(--qti-calculated-min-width, auto);
|
|
3079
|
-
/* Enables full styling when supported (Chromium behind a flag / rolling out). */
|
|
3080
|
-
appearance: base-select;
|
|
3081
|
-
}
|
|
3082
|
-
|
|
3083
|
-
select[part='select']:disabled {
|
|
3084
|
-
opacity: 0.6;
|
|
3085
|
-
cursor: not-allowed;
|
|
3086
|
-
}
|
|
3087
|
-
|
|
3088
|
-
select[part='select']::picker(select) {
|
|
3089
|
-
border: var(--qti-border-thickness, 2px) var(--qti-border-style, solid) var(--qti-border-color, #c6cad0);
|
|
3090
|
-
border-radius: var(--qti-border-radius, 0.3rem);
|
|
3091
|
-
background: var(--qti-bg, white);
|
|
3092
|
-
box-shadow:
|
|
3093
|
-
0 10px 15px -3px rgb(0 0 0 / 10%),
|
|
3094
|
-
0 4px 6px -4px rgb(0 0 0 / 10%);
|
|
3095
|
-
padding: 4px;
|
|
3096
|
-
width: max-content;
|
|
3097
|
-
min-width: 100%;
|
|
3098
|
-
max-width: min(90vw, 36rem);
|
|
3099
|
-
}
|
|
3100
|
-
|
|
3101
|
-
select[part='select']::picker-icon {
|
|
3102
|
-
color: var(--qti-border-color, #c6cad0);
|
|
3103
|
-
transition: 0.4s rotate;
|
|
3104
|
-
font-size: 1.75em;
|
|
3105
|
-
}
|
|
3106
|
-
|
|
3107
|
-
select[part='select']:open::picker-icon {
|
|
3108
|
-
color: var(--qti-border-active, #f86d70);
|
|
3109
|
-
rotate: 180deg;
|
|
3110
|
-
}
|
|
3111
|
-
|
|
3112
|
-
select[part='select'] > button {
|
|
3113
|
-
font: inherit;
|
|
3114
|
-
color: inherit;
|
|
3115
|
-
display: inline-flex;
|
|
3116
|
-
align-items: center;
|
|
3117
|
-
gap: 0.25rem;
|
|
3118
|
-
padding: 0;
|
|
3119
|
-
background: transparent;
|
|
3120
|
-
border: 0;
|
|
3121
|
-
cursor: pointer;
|
|
3122
|
-
}
|
|
3123
|
-
|
|
3124
|
-
select[part='select'] selectedcontent {
|
|
3125
|
-
display: inline-flex;
|
|
3126
|
-
align-items: center;
|
|
3127
|
-
gap: 0.5rem;
|
|
3128
|
-
white-space: nowrap;
|
|
3129
|
-
}
|
|
3130
|
-
|
|
3131
|
-
option {
|
|
3132
|
-
font: inherit;
|
|
3133
|
-
color: inherit;
|
|
3134
|
-
display: flex;
|
|
3135
|
-
align-items: center;
|
|
3136
|
-
gap: 0.5rem;
|
|
3137
|
-
padding: 0.5rem 0.5rem;
|
|
3138
|
-
white-space: nowrap;
|
|
3139
|
-
line-height: 1.25;
|
|
3140
|
-
min-height: 2.25rem;
|
|
3141
|
-
}
|
|
3142
|
-
|
|
3143
|
-
option:hover {
|
|
3144
|
-
background-color: var(--qti-hover-bg, #f9fafb);
|
|
3145
|
-
}
|
|
3146
|
-
|
|
3147
|
-
option:checked {
|
|
3148
|
-
background-color: var(--qti-bg-active, #ffecec);
|
|
3149
|
-
}
|
|
3150
|
-
|
|
3151
|
-
option::checkmark {
|
|
3152
|
-
color: var(--qti-border-active, #f86d70);
|
|
3153
|
-
}
|
|
3154
|
-
|
|
3155
|
-
/* --- Fallback custom listbox (for browsers without customizable select) --- */
|
|
3156
|
-
button[part='trigger'] {
|
|
3157
|
-
font: inherit;
|
|
3158
|
-
color: inherit;
|
|
3159
|
-
background-color: var(--qti-bg, white);
|
|
3160
|
-
cursor: pointer;
|
|
3161
|
-
display: inline-flex;
|
|
3162
|
-
align-items: center;
|
|
3163
|
-
gap: 0.5rem;
|
|
3164
|
-
justify-content: space-between;
|
|
3165
|
-
border: var(--qti-border-thickness, 2px) var(--qti-border-style, solid) var(--qti-border-color, #c6cad0);
|
|
3166
|
-
border-radius: var(--qti-border-radius, 0.3rem);
|
|
3167
|
-
padding: 0.25rem 0.75rem;
|
|
3168
|
-
min-width: var(--qti-calculated-min-width, auto);
|
|
3169
|
-
}
|
|
3170
|
-
|
|
3171
|
-
[part='value'] {
|
|
3172
|
-
display: inline-flex;
|
|
3173
|
-
align-items: center;
|
|
3174
|
-
gap: 0.5rem;
|
|
3175
|
-
min-width: 0;
|
|
3176
|
-
}
|
|
3177
|
-
|
|
3178
|
-
[part='dropdown-icon'] {
|
|
3179
|
-
display: inline-flex;
|
|
3180
|
-
align-items: center;
|
|
3181
|
-
justify-content: center;
|
|
3182
|
-
flex: 0 0 auto;
|
|
3183
|
-
transition: transform 150ms ease;
|
|
3184
|
-
transform-origin: 50% 50%;
|
|
3185
|
-
color: var(--qti-border-color, #c6cad0);
|
|
3186
|
-
font-size: 1.75em;
|
|
3187
|
-
line-height: 1;
|
|
3188
|
-
}
|
|
3189
|
-
|
|
3190
|
-
button[part='trigger'][aria-expanded='true'] [part='dropdown-icon'] {
|
|
3191
|
-
transform: rotate(180deg);
|
|
3192
|
-
color: var(--qti-border-active, #f86d70);
|
|
3193
|
-
}
|
|
3194
|
-
|
|
3195
|
-
button[part='trigger'][disabled] {
|
|
3196
|
-
cursor: not-allowed;
|
|
3197
|
-
opacity: 0.6;
|
|
3198
|
-
}
|
|
3199
|
-
|
|
3200
|
-
[part='menu'] {
|
|
3201
|
-
position: absolute;
|
|
3202
|
-
z-index: 1000;
|
|
3203
|
-
top: calc(100% + 4px);
|
|
3204
|
-
left: 0;
|
|
3205
|
-
min-width: 100%;
|
|
3206
|
-
max-width: min(90vw, 36rem);
|
|
3207
|
-
max-height: min(40vh, 20rem);
|
|
3208
|
-
overflow: auto;
|
|
3209
|
-
background-color: var(--qti-bg, white);
|
|
3210
|
-
border: var(--qti-border-thickness, 2px) var(--qti-border-style, solid) var(--qti-border-color, #c6cad0);
|
|
3211
|
-
border-radius: var(--qti-border-radius, 0.3rem);
|
|
3212
|
-
box-shadow:
|
|
3213
|
-
0 10px 15px -3px rgb(0 0 0 / 10%),
|
|
3214
|
-
0 4px 6px -4px rgb(0 0 0 / 10%);
|
|
3215
|
-
padding: 4px;
|
|
3216
|
-
box-sizing: border-box;
|
|
3217
|
-
transform: translate(var(--qti-menu-shift-x, 0px), var(--qti-menu-shift-y, 0px));
|
|
3218
|
-
}
|
|
3219
|
-
|
|
3220
|
-
[part='menu'][data-placement='top'] {
|
|
3221
|
-
top: auto;
|
|
3222
|
-
bottom: calc(100% + 4px);
|
|
3223
|
-
}
|
|
3224
|
-
|
|
3225
|
-
button[part='option'] {
|
|
3226
|
-
font: inherit;
|
|
3227
|
-
color: inherit;
|
|
3228
|
-
background-color: transparent;
|
|
3229
|
-
border: 0;
|
|
3230
|
-
padding: 0.5rem 0.5rem;
|
|
3231
|
-
width: 100%;
|
|
3232
|
-
text-align: left;
|
|
3233
|
-
border-radius: calc(var(--qti-border-radius, 0.3rem) - 2px);
|
|
3234
|
-
cursor: pointer;
|
|
3235
|
-
white-space: nowrap;
|
|
3236
|
-
overflow: hidden;
|
|
3237
|
-
text-overflow: ellipsis;
|
|
3238
|
-
line-height: 1.25;
|
|
3239
|
-
min-height: 2.25rem;
|
|
3240
|
-
}
|
|
3241
|
-
|
|
3242
|
-
button[part='option'][aria-selected='true'] {
|
|
3243
|
-
background-color: var(--qti-bg-active, #ffecec);
|
|
3244
|
-
}
|
|
3245
|
-
|
|
3246
|
-
button[part='option']:hover {
|
|
3247
|
-
background-color: var(--qti-hover-bg, #f9fafb);
|
|
3248
|
-
}
|
|
3249
|
-
|
|
3250
|
-
button[part='option']:focus-visible {
|
|
3251
|
-
outline: 2px solid var(--qti-border-active, #f86d70);
|
|
3252
|
-
outline-offset: 2px;
|
|
3253
|
-
}
|
|
3254
|
-
|
|
3255
|
-
[part='option-content'] {
|
|
3256
|
-
display: flex;
|
|
3257
|
-
align-items: center;
|
|
3258
|
-
gap: 0.5rem;
|
|
3259
|
-
flex-wrap: nowrap;
|
|
3260
|
-
white-space: nowrap;
|
|
3261
|
-
overflow: hidden;
|
|
3262
|
-
text-overflow: ellipsis;
|
|
3263
|
-
min-width: 0;
|
|
3264
|
-
}
|
|
3265
|
-
|
|
3266
|
-
select[part='select'] img,
|
|
3267
|
-
button[part='option'] img,
|
|
3268
|
-
button[part='trigger'] img,
|
|
3269
|
-
[part='menu'] img {
|
|
3270
|
-
display: inline-block;
|
|
3271
|
-
max-height: 1em;
|
|
3272
|
-
max-width: 1.5em;
|
|
3273
|
-
vertical-align: middle;
|
|
3274
|
-
}
|
|
3275
|
-
`
|
|
3276
|
-
];
|
|
3277
|
-
}
|
|
3278
|
-
static {
|
|
3279
|
-
this.inputWidthClass = [
|
|
3280
|
-
"",
|
|
3281
|
-
"qti-input-width-2",
|
|
3282
|
-
"qti-input-width-1",
|
|
3283
|
-
"qti-input-width-3",
|
|
3284
|
-
"qti-input-width-4",
|
|
3285
|
-
"qti-input-width-6",
|
|
3286
|
-
"qti-input-width-10",
|
|
3287
|
-
"qti-input-width-15",
|
|
3288
|
-
"qti-input-width-20",
|
|
3289
|
-
"qti-input-width-72"
|
|
3290
|
-
];
|
|
3131
|
+
return [qti_inline_choice_interaction_styles_default];
|
|
3291
3132
|
}
|
|
3292
3133
|
render() {
|
|
3293
3134
|
const selected = this.#selectedOption();
|
|
3294
|
-
const useCustomizableSelect = this.#supportsCustomizableSelect();
|
|
3295
3135
|
return x`
|
|
3296
|
-
|
|
3297
|
-
|
|
3298
|
-
|
|
3299
|
-
|
|
3300
|
-
|
|
3301
|
-
|
|
3302
|
-
|
|
3303
|
-
|
|
3304
|
-
|
|
3305
|
-
|
|
3306
|
-
|
|
3307
|
-
|
|
3308
|
-
|
|
3309
|
-
|
|
3310
|
-
|
|
3311
|
-
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3316
|
-
|
|
3317
|
-
|
|
3318
|
-
|
|
3319
|
-
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
3324
|
-
|
|
3325
|
-
|
|
3326
|
-
|
|
3327
|
-
|
|
3328
|
-
|
|
3329
|
-
|
|
3330
|
-
|
|
3331
|
-
|
|
3332
|
-
|
|
3333
|
-
@click="${() => this.#selectValue(option.value)}"
|
|
3334
|
-
>
|
|
3335
|
-
<span part="option-content">${o3(option.textContent)}</span>
|
|
3336
|
-
</button>
|
|
3337
|
-
`
|
|
3338
|
-
)}
|
|
3339
|
-
</div>
|
|
3340
|
-
` : null}
|
|
3341
|
-
`}
|
|
3136
|
+
<button
|
|
3137
|
+
part="trigger"
|
|
3138
|
+
type="button"
|
|
3139
|
+
@click=${this.#onTriggerClick}
|
|
3140
|
+
@keydown=${this.#onCustomTriggerKeyDown}
|
|
3141
|
+
aria-haspopup="listbox"
|
|
3142
|
+
aria-expanded="${this._dropdownOpen ? "true" : "false"}"
|
|
3143
|
+
aria-controls="${this._menuId}"
|
|
3144
|
+
popovertarget="${this._menuId}"
|
|
3145
|
+
popovertargetaction="toggle"
|
|
3146
|
+
?disabled="${this.disabled}"
|
|
3147
|
+
data-readonly="${this.readonly ? "true" : "false"}"
|
|
3148
|
+
>
|
|
3149
|
+
<span part="value">${o3(selected?.textContent ?? "")}</span>
|
|
3150
|
+
<span part="${this._dropdownOpen ? "dropdown-icon dropdown-icon-open" : "dropdown-icon"}" aria-hidden="true"
|
|
3151
|
+
>▾</span
|
|
3152
|
+
>
|
|
3153
|
+
</button>
|
|
3154
|
+
<div
|
|
3155
|
+
id="${this._menuId}"
|
|
3156
|
+
part="menu"
|
|
3157
|
+
role="listbox"
|
|
3158
|
+
popover="auto"
|
|
3159
|
+
@toggle=${this.#onMenuToggle}
|
|
3160
|
+
@keydown=${this.#onCustomMenuKeyDown}
|
|
3161
|
+
>
|
|
3162
|
+
<button
|
|
3163
|
+
part="${this.options[0]?.selected ? "option option-prompt option-selected" : "option option-prompt"}"
|
|
3164
|
+
type="button"
|
|
3165
|
+
role="option"
|
|
3166
|
+
aria-selected="${this.options[0]?.selected ? "true" : "false"}"
|
|
3167
|
+
@click=${() => this.#selectValue("")}
|
|
3168
|
+
>
|
|
3169
|
+
<span part="option-content">${o3(this.options[0]?.textContent ?? "")}</span>
|
|
3170
|
+
</button>
|
|
3171
|
+
<slot @slotchange=${this.#onChoicesSlotChange}></slot>
|
|
3172
|
+
</div>
|
|
3342
3173
|
${o3(this.correctOption)}
|
|
3343
3174
|
`;
|
|
3344
3175
|
}
|
|
3345
|
-
connectedCallback() {
|
|
3176
|
+
async connectedCallback() {
|
|
3346
3177
|
super.connectedCallback();
|
|
3347
3178
|
this.#updateOptions();
|
|
3348
|
-
|
|
3349
|
-
|
|
3350
|
-
|
|
3351
|
-
}
|
|
3352
|
-
this._estimateOptimalWidth();
|
|
3179
|
+
this.#startSlotObserver();
|
|
3180
|
+
await this.updateComplete;
|
|
3181
|
+
this.#estimateOptimalWidth();
|
|
3353
3182
|
}
|
|
3354
3183
|
disconnectedCallback() {
|
|
3355
3184
|
super.disconnectedCallback();
|
|
3356
|
-
|
|
3357
|
-
|
|
3358
|
-
|
|
3359
|
-
}
|
|
3360
|
-
if (this._widthCalculationTimer !== null) {
|
|
3361
|
-
window.clearTimeout(this._widthCalculationTimer);
|
|
3362
|
-
this._widthCalculationTimer = null;
|
|
3363
|
-
}
|
|
3185
|
+
this.#teardownSlottedChoices();
|
|
3186
|
+
this._slotObserver?.disconnect();
|
|
3187
|
+
this._slotObserver = null;
|
|
3364
3188
|
}
|
|
3365
3189
|
willUpdate(changed) {
|
|
3366
|
-
if (changed.has("configContext")
|
|
3190
|
+
if (changed.has("configContext")) {
|
|
3367
3191
|
this.#updateOptions();
|
|
3368
3192
|
}
|
|
3369
3193
|
}
|
|
3370
3194
|
updated(changed) {
|
|
3371
3195
|
const dropdownOpenKey = "_dropdownOpen";
|
|
3372
3196
|
if (changed.has(dropdownOpenKey) && this._dropdownOpen) {
|
|
3373
|
-
this.#
|
|
3374
|
-
const
|
|
3375
|
-
|
|
3197
|
+
this.#syncSlottedChoices();
|
|
3198
|
+
const first = this.#allMenuOptions()[0];
|
|
3199
|
+
first?.focus();
|
|
3200
|
+
}
|
|
3201
|
+
if (changed.has("disabled") || changed.has("readonly")) {
|
|
3202
|
+
this.#syncSlottedChoices();
|
|
3376
3203
|
}
|
|
3377
3204
|
}
|
|
3378
3205
|
#selectedOption() {
|
|
3379
3206
|
return this.options.find((option) => option.selected) ?? this.options[0];
|
|
3380
3207
|
}
|
|
3381
|
-
/**
|
|
3382
|
-
* Progressive enhancement for "customizable select" (WHATWG / MDN: `appearance: base-select` + `::picker()`).
|
|
3383
|
-
*
|
|
3384
|
-
* Notes on current browser behavior (observed around Feb 2026):
|
|
3385
|
-
* - Chromium-based browsers can support customizable select in light DOM, but it does not reliably work when the
|
|
3386
|
-
* `<select>` lives inside a shadow root (e.g. the internal `<button>/<selectedcontent>` can end up effectively
|
|
3387
|
-
* not rendered, so rich content like images disappears).
|
|
3388
|
-
* - Firefox support is not generally available yet, so we fall back to our custom listbox there as well.
|
|
3389
|
-
*
|
|
3390
|
-
* Because `CSS.supports(...)` may return syntax-only true, we do a final DOM probe to ensure the customizable-select
|
|
3391
|
-
* markup actually takes effect in the current environment before opting in.
|
|
3392
|
-
*/
|
|
3393
|
-
#supportsCustomizableSelect() {
|
|
3394
|
-
if (_QtiInlineChoiceInteraction._supportsCustomizableSelectCache !== null) {
|
|
3395
|
-
return _QtiInlineChoiceInteraction._supportsCustomizableSelectCache;
|
|
3396
|
-
}
|
|
3397
|
-
if (typeof CSS === "undefined" || typeof CSS.supports !== "function") {
|
|
3398
|
-
_QtiInlineChoiceInteraction._supportsCustomizableSelectCache = false;
|
|
3399
|
-
return false;
|
|
3400
|
-
}
|
|
3401
|
-
const supportsPickerSelector = CSS.supports("selector(::picker(select))") || CSS.supports("selector(select::picker(select))");
|
|
3402
|
-
const supportsAppearanceValue = CSS.supports("appearance: base-select") || CSS.supports("-webkit-appearance: base-select");
|
|
3403
|
-
if (!supportsPickerSelector || !supportsAppearanceValue) {
|
|
3404
|
-
_QtiInlineChoiceInteraction._supportsCustomizableSelectCache = false;
|
|
3405
|
-
return false;
|
|
3406
|
-
}
|
|
3407
|
-
try {
|
|
3408
|
-
const container = document.createElement("div");
|
|
3409
|
-
container.style.position = "absolute";
|
|
3410
|
-
container.style.top = "-9999px";
|
|
3411
|
-
container.style.left = "-9999px";
|
|
3412
|
-
const select = document.createElement("select");
|
|
3413
|
-
select.style.appearance = "base-select";
|
|
3414
|
-
select.style.webkitAppearance = "base-select";
|
|
3415
|
-
const button = document.createElement("button");
|
|
3416
|
-
button.type = "button";
|
|
3417
|
-
const selected = document.createElement("selectedcontent");
|
|
3418
|
-
selected.textContent = "probe";
|
|
3419
|
-
button.appendChild(selected);
|
|
3420
|
-
const option = document.createElement("option");
|
|
3421
|
-
option.value = "probe";
|
|
3422
|
-
option.textContent = "probe";
|
|
3423
|
-
select.appendChild(button);
|
|
3424
|
-
select.appendChild(option);
|
|
3425
|
-
container.appendChild(select);
|
|
3426
|
-
(document.body || document.documentElement).appendChild(container);
|
|
3427
|
-
const rect = button.getBoundingClientRect();
|
|
3428
|
-
container.remove();
|
|
3429
|
-
const supported = rect.width > 0 && rect.height > 0;
|
|
3430
|
-
_QtiInlineChoiceInteraction._supportsCustomizableSelectCache = supported;
|
|
3431
|
-
return supported;
|
|
3432
|
-
} catch {
|
|
3433
|
-
_QtiInlineChoiceInteraction._supportsCustomizableSelectCache = false;
|
|
3434
|
-
return false;
|
|
3435
|
-
}
|
|
3436
|
-
}
|
|
3437
3208
|
#updateOptions() {
|
|
3438
3209
|
const choices = Array.from(this.querySelectorAll("qti-inline-choice"));
|
|
3439
|
-
const prompt = this.
|
|
3210
|
+
const prompt = this.dataset.prompt || this.configContext?.inlineChoicePrompt || "select";
|
|
3440
3211
|
const currentlySelectedValue = this.options.find((o6) => o6.selected)?.value ?? "";
|
|
3441
3212
|
const nextOptions = [
|
|
3442
3213
|
{
|
|
@@ -3455,22 +3226,31 @@ var _QtiInlineChoiceInteraction = class _QtiInlineChoiceInteraction extends Inte
|
|
|
3455
3226
|
];
|
|
3456
3227
|
const hasSelected = nextOptions.some((o6) => o6.selected);
|
|
3457
3228
|
this.options = hasSelected ? nextOptions : nextOptions.map((o6, i5) => ({ ...o6, selected: i5 === 0 }));
|
|
3458
|
-
this
|
|
3229
|
+
this.#syncSlottedChoices();
|
|
3230
|
+
this.#estimateOptimalWidth();
|
|
3459
3231
|
}
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
3469
|
-
|
|
3470
|
-
|
|
3471
|
-
|
|
3472
|
-
|
|
3473
|
-
|
|
3232
|
+
#estimateOptimalWidth() {
|
|
3233
|
+
const menu = this.#menuElement();
|
|
3234
|
+
const trigger = this.renderRoot.querySelector('button[part="trigger"]');
|
|
3235
|
+
if (!menu || !trigger) return;
|
|
3236
|
+
const dropdownIcon = trigger.querySelector('span[part~="dropdown-icon"]');
|
|
3237
|
+
const iconWidth = dropdownIcon ? dropdownIcon.getBoundingClientRect().width : 0;
|
|
3238
|
+
const wasOpen = menu.matches(":popover-open");
|
|
3239
|
+
const prevVisibility = menu.style.visibility;
|
|
3240
|
+
const prevDisplay = menu.style.display;
|
|
3241
|
+
if (!wasOpen) {
|
|
3242
|
+
menu.style.visibility = "hidden";
|
|
3243
|
+
menu.style.display = "block";
|
|
3244
|
+
menu.showPopover();
|
|
3245
|
+
}
|
|
3246
|
+
const rectWidth = menu.getBoundingClientRect().width;
|
|
3247
|
+
const widthPx = Math.max(rectWidth, menu.scrollWidth);
|
|
3248
|
+
if (!wasOpen) {
|
|
3249
|
+
menu.hidePopover();
|
|
3250
|
+
menu.style.visibility = prevVisibility;
|
|
3251
|
+
menu.style.display = prevDisplay;
|
|
3252
|
+
}
|
|
3253
|
+
trigger.style.width = `${widthPx + iconWidth}px`;
|
|
3474
3254
|
}
|
|
3475
3255
|
validate() {
|
|
3476
3256
|
const selectedOption = this.options.find((option) => option.selected);
|
|
@@ -3479,10 +3259,12 @@ var _QtiInlineChoiceInteraction = class _QtiInlineChoiceInteraction extends Inte
|
|
|
3479
3259
|
reset() {
|
|
3480
3260
|
this.#setDropdownOpen(false);
|
|
3481
3261
|
this.options = this.options.map((option, i5) => ({ ...option, selected: i5 === 0 }));
|
|
3262
|
+
this.#syncSlottedChoices();
|
|
3482
3263
|
}
|
|
3483
3264
|
set response(value) {
|
|
3484
3265
|
const nextValue = value ?? "";
|
|
3485
3266
|
this.options = this.options.map((option) => ({ ...option, selected: option.value === nextValue }));
|
|
3267
|
+
this.#syncSlottedChoices();
|
|
3486
3268
|
}
|
|
3487
3269
|
get response() {
|
|
3488
3270
|
const value = this.options.find((option) => option.selected)?.value ?? "";
|
|
@@ -3502,74 +3284,107 @@ var _QtiInlineChoiceInteraction = class _QtiInlineChoiceInteraction extends Inte
|
|
|
3502
3284
|
}
|
|
3503
3285
|
this.correctOption = `<span part="correct-option" style="border:1px solid var(--qti-correct); border-radius:4px; padding: 2px 4px; margin: 4px; display:inline-block">${correctOptionData.textContent}</span>`;
|
|
3504
3286
|
}
|
|
3505
|
-
#onNativeChange;
|
|
3506
3287
|
#selectValue(value) {
|
|
3507
3288
|
this.options = this.options.map((option) => ({ ...option, selected: option.value === value }));
|
|
3289
|
+
this.#syncSlottedChoices();
|
|
3508
3290
|
this.saveResponse(value);
|
|
3509
3291
|
this.#setDropdownOpen(false);
|
|
3510
3292
|
}
|
|
3511
3293
|
#setDropdownOpen(open) {
|
|
3512
|
-
|
|
3513
|
-
|
|
3294
|
+
const menu = this.#menuElement();
|
|
3295
|
+
if (!menu) return;
|
|
3296
|
+
if (open) {
|
|
3297
|
+
if (!menu.matches(":popover-open")) {
|
|
3298
|
+
menu.showPopover();
|
|
3299
|
+
}
|
|
3300
|
+
return;
|
|
3301
|
+
}
|
|
3302
|
+
if (menu.matches(":popover-open")) {
|
|
3303
|
+
menu.hidePopover();
|
|
3304
|
+
}
|
|
3514
3305
|
}
|
|
3515
|
-
#
|
|
3306
|
+
#onTriggerClick;
|
|
3516
3307
|
#onCustomTriggerKeyDown;
|
|
3308
|
+
#onMenuToggle;
|
|
3517
3309
|
#onCustomMenuKeyDown;
|
|
3518
3310
|
#focusTrigger() {
|
|
3519
3311
|
this.renderRoot.querySelector('button[part="trigger"]')?.focus();
|
|
3520
3312
|
}
|
|
3521
|
-
#
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
|
|
3537
|
-
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
const
|
|
3542
|
-
const
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
3546
|
-
|
|
3547
|
-
|
|
3548
|
-
|
|
3549
|
-
|
|
3550
|
-
|
|
3313
|
+
#getDeepActiveElement() {
|
|
3314
|
+
let current = document.activeElement;
|
|
3315
|
+
while (current && current instanceof HTMLElement && current.shadowRoot?.activeElement) {
|
|
3316
|
+
current = current.shadowRoot.activeElement;
|
|
3317
|
+
}
|
|
3318
|
+
return current;
|
|
3319
|
+
}
|
|
3320
|
+
#isElementInsideInteraction(element) {
|
|
3321
|
+
return element === this || this.contains(element) || this.renderRoot.contains(element);
|
|
3322
|
+
}
|
|
3323
|
+
#menuElement() {
|
|
3324
|
+
return this.renderRoot.querySelector(`#${this._menuId}`);
|
|
3325
|
+
}
|
|
3326
|
+
#startSlotObserver() {
|
|
3327
|
+
this._slotObserver = new MutationObserver(() => this.#updateOptions());
|
|
3328
|
+
this._slotObserver.observe(this, { childList: true, subtree: true });
|
|
3329
|
+
}
|
|
3330
|
+
#onChoicesSlotChange;
|
|
3331
|
+
#onSlottedChoiceClick;
|
|
3332
|
+
#teardownSlottedChoices() {
|
|
3333
|
+
const choices = Array.from(this.querySelectorAll("qti-inline-choice"));
|
|
3334
|
+
for (const choice of choices) {
|
|
3335
|
+
choice.removeEventListener("click", this.#onSlottedChoiceClick);
|
|
3336
|
+
choice.removeAttribute("tabindex");
|
|
3337
|
+
choice.internals.role = null;
|
|
3338
|
+
choice.internals.ariaSelected = null;
|
|
3339
|
+
choice.internals.ariaChecked = "false";
|
|
3340
|
+
choice.internals.states.delete("--checked");
|
|
3341
|
+
}
|
|
3342
|
+
}
|
|
3343
|
+
async #syncSlottedChoices() {
|
|
3344
|
+
await this.updateComplete;
|
|
3345
|
+
const selectedValue = this.options.find((option) => option.selected)?.value ?? "";
|
|
3346
|
+
const choices = Array.from(this.querySelectorAll("qti-inline-choice"));
|
|
3347
|
+
for (const choice of choices) {
|
|
3348
|
+
const value = choice.getAttribute("identifier") ?? "";
|
|
3349
|
+
const isSelected = value === selectedValue;
|
|
3350
|
+
choice.removeEventListener("click", this.#onSlottedChoiceClick);
|
|
3351
|
+
choice.addEventListener("click", this.#onSlottedChoiceClick);
|
|
3352
|
+
choice.disabled = this.disabled;
|
|
3353
|
+
choice.readonly = this.readonly;
|
|
3354
|
+
choice.internals.role = "option";
|
|
3355
|
+
choice.internals.ariaSelected = isSelected ? "true" : "false";
|
|
3356
|
+
choice.internals.ariaChecked = isSelected ? "true" : "false";
|
|
3357
|
+
choice.internals.ariaDisabled = this.disabled ? "true" : "false";
|
|
3358
|
+
choice.internals.ariaReadOnly = this.readonly ? "true" : "false";
|
|
3359
|
+
choice.removeAttribute("aria-disabled");
|
|
3360
|
+
choice.removeAttribute("aria-readonly");
|
|
3361
|
+
if (isSelected) {
|
|
3362
|
+
choice.internals.states.add("--checked");
|
|
3363
|
+
} else {
|
|
3364
|
+
choice.internals.states.delete("--checked");
|
|
3365
|
+
}
|
|
3366
|
+
choice.tabIndex = -1;
|
|
3367
|
+
}
|
|
3368
|
+
}
|
|
3369
|
+
#allMenuOptions() {
|
|
3370
|
+
const promptOption = this.renderRoot.querySelector('button[part~="option"]');
|
|
3371
|
+
const slottedChoices = Array.from(this.querySelectorAll("qti-inline-choice"));
|
|
3372
|
+
return [...promptOption ? [promptOption] : [], ...slottedChoices];
|
|
3551
3373
|
}
|
|
3552
3374
|
};
|
|
3553
3375
|
__decorateClass([
|
|
3554
3376
|
r()
|
|
3555
|
-
],
|
|
3556
|
-
__decorateClass([
|
|
3557
|
-
r()
|
|
3558
|
-
], _QtiInlineChoiceInteraction.prototype, "correctOption", 2);
|
|
3377
|
+
], QtiInlineChoiceInteraction.prototype, "options", 2);
|
|
3559
3378
|
__decorateClass([
|
|
3560
3379
|
r()
|
|
3561
|
-
],
|
|
3380
|
+
], QtiInlineChoiceInteraction.prototype, "correctOption", 2);
|
|
3562
3381
|
__decorateClass([
|
|
3563
3382
|
r()
|
|
3564
|
-
],
|
|
3565
|
-
__decorateClass([
|
|
3566
|
-
n({ attribute: "data-prompt", type: String })
|
|
3567
|
-
], _QtiInlineChoiceInteraction.prototype, "dataPrompt", 2);
|
|
3383
|
+
], QtiInlineChoiceInteraction.prototype, "_dropdownOpen", 2);
|
|
3568
3384
|
__decorateClass([
|
|
3569
3385
|
c({ context: configContext, subscribe: true }),
|
|
3570
3386
|
n({ attribute: false })
|
|
3571
|
-
],
|
|
3572
|
-
var QtiInlineChoiceInteraction = _QtiInlineChoiceInteraction;
|
|
3387
|
+
], QtiInlineChoiceInteraction.prototype, "configContext", 2);
|
|
3573
3388
|
|
|
3574
3389
|
// ../qti-interactions/src/components/qti-match-interaction/qti-match-interaction.styles.ts
|
|
3575
3390
|
var qti_match_interaction_styles_default = i`
|
|
@@ -4609,94 +4424,6 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4609
4424
|
}
|
|
4610
4425
|
return unescaped;
|
|
4611
4426
|
}
|
|
4612
|
-
/**
|
|
4613
|
-
* Resolve stylesheet href against baseUrl or document origin
|
|
4614
|
-
*/
|
|
4615
|
-
resolveStylesheetHref(href) {
|
|
4616
|
-
if (!href) return href;
|
|
4617
|
-
if (href.startsWith("http://") || href.startsWith("https://")) {
|
|
4618
|
-
return href;
|
|
4619
|
-
}
|
|
4620
|
-
if (href.startsWith("//")) {
|
|
4621
|
-
return `${window.location.protocol}${href}`;
|
|
4622
|
-
}
|
|
4623
|
-
const base = this.baseUrl && this.baseUrl.length > 0 ? this.baseUrl.startsWith("http") || this.baseUrl.startsWith("blob") || this.baseUrl.startsWith("base64") ? this.baseUrl : removeDoubleSlashes(`${window.location.origin}${this.baseUrl}`) : window.location.origin;
|
|
4624
|
-
const normalizedBase = base.endsWith("/") ? base : `${base}/`;
|
|
4625
|
-
try {
|
|
4626
|
-
return new URL(href, normalizedBase).toString();
|
|
4627
|
-
} catch {
|
|
4628
|
-
return href;
|
|
4629
|
-
}
|
|
4630
|
-
}
|
|
4631
|
-
/**
|
|
4632
|
-
* Collect qti-stylesheet elements for iframe injection
|
|
4633
|
-
*/
|
|
4634
|
-
getStylesheetConfigs() {
|
|
4635
|
-
const stylesheets = this.getDirectChildrenByTag("qti-stylesheet");
|
|
4636
|
-
if (!stylesheets.length) return [];
|
|
4637
|
-
return stylesheets.map((el, index) => {
|
|
4638
|
-
const href = el.getAttribute("href");
|
|
4639
|
-
if (href) {
|
|
4640
|
-
const resolved = this.resolveStylesheetHref(href);
|
|
4641
|
-
return { href: resolved, scoped: false, key: resolved };
|
|
4642
|
-
}
|
|
4643
|
-
const content = el.textContent?.trim();
|
|
4644
|
-
if (content) {
|
|
4645
|
-
return { content, scoped: false, key: `inline-${index}` };
|
|
4646
|
-
}
|
|
4647
|
-
return null;
|
|
4648
|
-
}).filter(Boolean);
|
|
4649
|
-
}
|
|
4650
|
-
getSharedStylesheetContent() {
|
|
4651
|
-
let cssText = "";
|
|
4652
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4653
|
-
const sheets = Array.from(document.styleSheets || []);
|
|
4654
|
-
for (const sheet of sheets) {
|
|
4655
|
-
try {
|
|
4656
|
-
if (sheet.href && !sheet.href.startsWith(window.location.origin)) {
|
|
4657
|
-
continue;
|
|
4658
|
-
}
|
|
4659
|
-
const ownerNode = sheet.ownerNode;
|
|
4660
|
-
if (ownerNode && ownerNode.tagName === "STYLE") {
|
|
4661
|
-
const text = ownerNode.textContent || "";
|
|
4662
|
-
if (text && !seen.has(text)) {
|
|
4663
|
-
cssText += `${text}
|
|
4664
|
-
`;
|
|
4665
|
-
seen.add(text);
|
|
4666
|
-
}
|
|
4667
|
-
continue;
|
|
4668
|
-
}
|
|
4669
|
-
const rules = sheet.cssRules ? Array.from(sheet.cssRules) : [];
|
|
4670
|
-
if (rules.length) {
|
|
4671
|
-
const text = rules.map((rule) => rule.cssText).join("\n");
|
|
4672
|
-
if (text && !seen.has(text)) {
|
|
4673
|
-
cssText += `${text}
|
|
4674
|
-
`;
|
|
4675
|
-
seen.add(text);
|
|
4676
|
-
}
|
|
4677
|
-
}
|
|
4678
|
-
} catch {
|
|
4679
|
-
}
|
|
4680
|
-
}
|
|
4681
|
-
const trimmed = cssText.trim();
|
|
4682
|
-
return trimmed.length ? trimmed : null;
|
|
4683
|
-
}
|
|
4684
|
-
getSharedStylesheetConfig() {
|
|
4685
|
-
const content = this.getSharedStylesheetContent();
|
|
4686
|
-
if (!content) return null;
|
|
4687
|
-
return { content, scoped: false, key: "__qti_shared_css__" };
|
|
4688
|
-
}
|
|
4689
|
-
/**
|
|
4690
|
-
* IFRAME MODE: Add stylesheets to iframe
|
|
4691
|
-
*/
|
|
4692
|
-
#addStylesheetsToIframe() {
|
|
4693
|
-
const stylesheets = this.getStylesheetConfigs();
|
|
4694
|
-
const shared = this.getSharedStylesheetConfig();
|
|
4695
|
-
const payload = shared ? [shared, ...stylesheets] : stylesheets;
|
|
4696
|
-
if (payload.length > 0) {
|
|
4697
|
-
this.sendMessageToIframe("setStylesheets", payload);
|
|
4698
|
-
}
|
|
4699
|
-
}
|
|
4700
4427
|
disconnectedCallback() {
|
|
4701
4428
|
super.disconnectedCallback();
|
|
4702
4429
|
window.removeEventListener("message", this.handleIframeMessage);
|
|
@@ -4759,7 +4486,6 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4759
4486
|
this._iframeObjectUrl = null;
|
|
4760
4487
|
}
|
|
4761
4488
|
this.#addMarkupToIframe();
|
|
4762
|
-
this.#addStylesheetsToIframe();
|
|
4763
4489
|
this.#sendIframeInitData();
|
|
4764
4490
|
};
|
|
4765
4491
|
const iframeName = `qti-pci-${this.responseIdentifier}-${Date.now()}`;
|
|
@@ -4816,13 +4542,18 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4816
4542
|
return modules;
|
|
4817
4543
|
}
|
|
4818
4544
|
/**
|
|
4819
|
-
* IFRAME MODE: Add markup and properties to iframe
|
|
4545
|
+
* IFRAME MODE: Add markup, stylesheets, and properties to iframe
|
|
4820
4546
|
*/
|
|
4821
4547
|
#addMarkupToIframe() {
|
|
4822
4548
|
const markup = this.querySelector("qti-interaction-markup");
|
|
4823
4549
|
if (markup) {
|
|
4824
4550
|
this.sendMessageToIframe("setMarkup", markup.innerHTML);
|
|
4825
4551
|
}
|
|
4552
|
+
const stylesheets = Array.from(this.querySelectorAll("qti-stylesheet"));
|
|
4553
|
+
if (stylesheets.length) {
|
|
4554
|
+
const stylesheetsHtml = stylesheets.map((sheet) => sheet.outerHTML).join("\n");
|
|
4555
|
+
this.sendMessageToIframe("setStylesheets", stylesheetsHtml);
|
|
4556
|
+
}
|
|
4826
4557
|
const properties = this.querySelector("properties");
|
|
4827
4558
|
if (properties) {
|
|
4828
4559
|
this.sendMessageToIframe("setProperties", properties.innerHTML);
|
|
@@ -4855,863 +4586,873 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4855
4586
|
font-weight: ${parentStyles.getPropertyValue("font-weight")};
|
|
4856
4587
|
color: ${parentStyles.getPropertyValue("color")};
|
|
4857
4588
|
`;
|
|
4858
|
-
return
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
|
|
4878
|
-
|
|
4879
|
-
|
|
4880
|
-
|
|
4881
|
-
|
|
4882
|
-
display: block;
|
|
4883
|
-
width: 100%;
|
|
4884
|
-
min-height: 50px;
|
|
4885
|
-
}
|
|
4886
|
-
</style>
|
|
4887
|
-
<script src="${this.requireJsUrl}"></script>
|
|
4888
|
-
<script>
|
|
4889
|
-
const forwardConsole = ${forwardConsole ? "true" : "false"};
|
|
4890
|
-
if (forwardConsole) {
|
|
4891
|
-
const originalLog = console.log.bind(console);
|
|
4892
|
-
const originalError = console.error.bind(console);
|
|
4893
|
-
const stringifyArgs = args =>
|
|
4894
|
-
args.map(arg => {
|
|
4895
|
-
if (typeof arg === 'string') return arg;
|
|
4896
|
-
try {
|
|
4897
|
-
return JSON.stringify(arg);
|
|
4898
|
-
} catch (e) {
|
|
4899
|
-
return String(arg);
|
|
4900
|
-
}
|
|
4901
|
-
});
|
|
4902
|
-
console.log = (...args) => {
|
|
4903
|
-
originalLog(...args);
|
|
4904
|
-
window.parent.postMessage(
|
|
4905
|
-
{
|
|
4906
|
-
source: 'qti-pci-iframe',
|
|
4907
|
-
responseIdentifier: (window.PCIManager && window.PCIManager.responseIdentifier) || null,
|
|
4908
|
-
method: 'console',
|
|
4909
|
-
level: 'log',
|
|
4910
|
-
args: stringifyArgs(args)
|
|
4911
|
-
},
|
|
4912
|
-
'*'
|
|
4913
|
-
);
|
|
4914
|
-
};
|
|
4915
|
-
console.error = (...args) => {
|
|
4916
|
-
originalError(...args);
|
|
4917
|
-
window.parent.postMessage(
|
|
4918
|
-
{
|
|
4919
|
-
source: 'qti-pci-iframe',
|
|
4920
|
-
responseIdentifier: (window.PCIManager && window.PCIManager.responseIdentifier) || null,
|
|
4921
|
-
method: 'console',
|
|
4922
|
-
level: 'error',
|
|
4923
|
-
args: stringifyArgs(args)
|
|
4924
|
-
},
|
|
4925
|
-
'*'
|
|
4926
|
-
);
|
|
4927
|
-
};
|
|
4928
|
-
}
|
|
4929
|
-
// Define standard paths and shims
|
|
4930
|
-
window.requirePaths = ${requirePaths};
|
|
4931
|
-
|
|
4932
|
-
window.requireShim = ${requireShim};
|
|
4933
|
-
|
|
4934
|
-
// Single initial RequireJS configuration with error handling
|
|
4935
|
-
window.requirejs.config({
|
|
4936
|
-
catchError: true,
|
|
4937
|
-
waitSeconds: 30,
|
|
4938
|
-
paths: window.requirePaths,
|
|
4939
|
-
baseUrl: '${iframeBaseUrl}',
|
|
4940
|
-
shim: window.requireShim,
|
|
4941
|
-
onNodeCreated: function(node, config, moduleName, url) {
|
|
4942
|
-
// Add error handler to script node
|
|
4943
|
-
node.addEventListener('error', function(evt) {
|
|
4944
|
-
console.error('Script load error for module:', moduleName, 'URL:', url, 'Event:', evt);
|
|
4945
|
-
});
|
|
4946
|
-
},
|
|
4947
|
-
onError: function(err) {
|
|
4948
|
-
console.error('RequireJS error:', {
|
|
4949
|
-
type: err.requireType,
|
|
4950
|
-
modules: err.requireModules,
|
|
4951
|
-
error: err
|
|
4952
|
-
});
|
|
4953
|
-
|
|
4954
|
-
if (err.requireType === 'scripterror') {
|
|
4955
|
-
console.error('Script error usually indicates a network or CORS issue with:', err.requireModules);
|
|
4956
|
-
}
|
|
4957
|
-
|
|
4958
|
-
// Notify parent window about the error
|
|
4959
|
-
window.parent.postMessage({
|
|
4960
|
-
source: 'qti-pci-iframe',
|
|
4961
|
-
responseIdentifier: (window.PCIManager && window.PCIManager.responseIdentifier) || null,
|
|
4962
|
-
method: 'error',
|
|
4963
|
-
params: {
|
|
4964
|
-
message: 'RequireJS ' + err.requireType + ' error for modules: ' + err.requireModules,
|
|
4965
|
-
details: {
|
|
4966
|
-
type: err.requireType,
|
|
4967
|
-
modules: err.requireModules,
|
|
4968
|
-
error: err.toString()
|
|
4589
|
+
return (
|
|
4590
|
+
/* html */
|
|
4591
|
+
`<!DOCTYPE html>
|
|
4592
|
+
<html lang="en">
|
|
4593
|
+
<head>
|
|
4594
|
+
<meta charset="utf-8" />
|
|
4595
|
+
<title>QTI PCI Container</title>
|
|
4596
|
+
<base href="${window.location.origin}" />
|
|
4597
|
+
<script type="module">
|
|
4598
|
+
import 'https://unpkg.com/@citolab/qti-components/cdn';
|
|
4599
|
+
</script>
|
|
4600
|
+
<style>
|
|
4601
|
+
body, html {
|
|
4602
|
+
margin: 0;
|
|
4603
|
+
padding: 0;
|
|
4604
|
+
width: 100%;
|
|
4605
|
+
height: auto;
|
|
4606
|
+
overflow: hidden;
|
|
4607
|
+
/* Add the extracted font styles here */
|
|
4608
|
+
${fontStyles}
|
|
4609
|
+
};
|
|
4610
|
+
.qti-customInteraction {
|
|
4611
|
+
width: 100%;
|
|
4612
|
+
height: 100%;
|
|
4969
4613
|
}
|
|
4970
|
-
|
|
4971
|
-
|
|
4972
|
-
}
|
|
4973
|
-
});
|
|
4974
|
-
|
|
4975
|
-
// PCI Manager for iframe implementation
|
|
4976
|
-
window.PCIManager = {
|
|
4977
|
-
pciInstance: null,
|
|
4978
|
-
container: null,
|
|
4979
|
-
markupEl: null,
|
|
4980
|
-
propertiesEl: null,
|
|
4981
|
-
customInteractionTypeIdentifier: null,
|
|
4982
|
-
responseIdentifier: null,
|
|
4983
|
-
pendingBoundTo: null,
|
|
4984
|
-
pendingMarkup: null,
|
|
4985
|
-
pendingProperties: null,
|
|
4986
|
-
pendingState: null,
|
|
4987
|
-
pendingStylesheets: null,
|
|
4988
|
-
stylesheetKeys: {},
|
|
4989
|
-
interactionChangedViaEvent: false,
|
|
4990
|
-
eventBridgeAttached: false,
|
|
4991
|
-
lastResponseStr: null,
|
|
4992
|
-
hadResponse: false,
|
|
4993
|
-
|
|
4994
|
-
initialize: function(config) {
|
|
4995
|
-
this.customInteractionTypeIdentifier = config.customInteractionTypeIdentifier;
|
|
4996
|
-
this.responseIdentifier = config.responseIdentifier;
|
|
4997
|
-
this.container = document.getElementById('pci-container');
|
|
4998
|
-
this.container.classList.add('qti-customInteraction');
|
|
4999
|
-
|
|
5000
|
-
function qtiVariableHasValue(qtiVar) {
|
|
5001
|
-
if (!qtiVar) return false;
|
|
5002
|
-
if (qtiVar.base) {
|
|
5003
|
-
for (const k in qtiVar.base) {
|
|
5004
|
-
if (!Object.prototype.hasOwnProperty.call(qtiVar.base, k)) continue;
|
|
5005
|
-
const v = qtiVar.base[k];
|
|
5006
|
-
if (v !== null && v !== undefined && v !== '') return true;
|
|
4614
|
+
#pci-container {
|
|
4615
|
+
width: 100%;
|
|
5007
4616
|
}
|
|
5008
|
-
|
|
5009
|
-
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
const v = qtiVar.list[k];
|
|
5013
|
-
if (Array.isArray(v) && v.some(x => x !== null && x !== undefined && x !== '')) return true;
|
|
4617
|
+
qti-interaction-markup {
|
|
4618
|
+
display: block;
|
|
4619
|
+
width: 100%;
|
|
4620
|
+
min-height: 50px;
|
|
5014
4621
|
}
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
4622
|
+
</style>
|
|
4623
|
+
<link href="https://unpkg.com/@citolab/qti-components@latest/dist/item.css" rel="stylesheet" />
|
|
4624
|
+
<script src="${this.requireJsUrl}"></script>
|
|
4625
|
+
<script>
|
|
4626
|
+
const forwardConsole = ${forwardConsole ? "true" : "false"};
|
|
4627
|
+
if (forwardConsole) {
|
|
4628
|
+
const originalLog = console.log.bind(console);
|
|
4629
|
+
const originalError = console.error.bind(console);
|
|
4630
|
+
const stringifyArgs = args =>
|
|
4631
|
+
args.map(arg => {
|
|
4632
|
+
if (typeof arg === 'string') return arg;
|
|
4633
|
+
try {
|
|
4634
|
+
return JSON.stringify(arg);
|
|
4635
|
+
} catch (e) {
|
|
4636
|
+
return String(arg);
|
|
4637
|
+
}
|
|
4638
|
+
});
|
|
4639
|
+
console.log = (...args) => {
|
|
4640
|
+
originalLog(...args);
|
|
4641
|
+
window.parent.postMessage(
|
|
4642
|
+
{
|
|
4643
|
+
source: 'qti-pci-iframe',
|
|
4644
|
+
responseIdentifier: (window.PCIManager && window.PCIManager.responseIdentifier) || null,
|
|
4645
|
+
method: 'console',
|
|
4646
|
+
level: 'log',
|
|
4647
|
+
args: stringifyArgs(args)
|
|
4648
|
+
},
|
|
4649
|
+
'*'
|
|
4650
|
+
);
|
|
4651
|
+
};
|
|
4652
|
+
console.error = (...args) => {
|
|
4653
|
+
originalError(...args);
|
|
4654
|
+
window.parent.postMessage(
|
|
4655
|
+
{
|
|
4656
|
+
source: 'qti-pci-iframe',
|
|
4657
|
+
responseIdentifier: (window.PCIManager && window.PCIManager.responseIdentifier) || null,
|
|
4658
|
+
method: 'console',
|
|
4659
|
+
level: 'error',
|
|
4660
|
+
args: stringifyArgs(args)
|
|
4661
|
+
},
|
|
4662
|
+
'*'
|
|
4663
|
+
);
|
|
4664
|
+
};
|
|
4665
|
+
}
|
|
4666
|
+
// Define standard paths and shims
|
|
4667
|
+
window.requirePaths = ${requirePaths};
|
|
4668
|
+
|
|
4669
|
+
window.requireShim = ${requireShim};
|
|
4670
|
+
|
|
4671
|
+
// Single initial RequireJS configuration with error handling
|
|
4672
|
+
window.requirejs.config({
|
|
4673
|
+
catchError: true,
|
|
4674
|
+
waitSeconds: 30,
|
|
4675
|
+
paths: window.requirePaths,
|
|
4676
|
+
baseUrl: '${iframeBaseUrl}',
|
|
4677
|
+
shim: window.requireShim,
|
|
4678
|
+
onNodeCreated: function (node, config, moduleName, url) {
|
|
4679
|
+
// Add error handler to script node
|
|
4680
|
+
node.addEventListener('error', function (evt) {
|
|
4681
|
+
console.error('Script load error for module:', moduleName, 'URL:', url, 'Event:', evt);
|
|
4682
|
+
});
|
|
4683
|
+
},
|
|
4684
|
+
onError: function (err) {
|
|
4685
|
+
console.error('RequireJS error:', {
|
|
4686
|
+
type: err.requireType,
|
|
4687
|
+
modules: err.requireModules,
|
|
4688
|
+
error: err
|
|
4689
|
+
});
|
|
4690
|
+
|
|
4691
|
+
if (err.requireType === 'scripterror') {
|
|
4692
|
+
console.error('Script error usually indicates a network or CORS issue with:', err.requireModules);
|
|
4693
|
+
}
|
|
5019
4694
|
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
5033
|
-
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
4695
|
+
// Notify parent window about the error
|
|
4696
|
+
window.parent.postMessage(
|
|
4697
|
+
{
|
|
4698
|
+
source: 'qti-pci-iframe',
|
|
4699
|
+
responseIdentifier: (window.PCIManager && window.PCIManager.responseIdentifier) || null,
|
|
4700
|
+
method: 'error',
|
|
4701
|
+
params: {
|
|
4702
|
+
message: 'RequireJS ' + err.requireType + ' error for modules: ' + err.requireModules,
|
|
4703
|
+
details: {
|
|
4704
|
+
type: err.requireType,
|
|
4705
|
+
modules: err.requireModules,
|
|
4706
|
+
error: err.toString()
|
|
4707
|
+
}
|
|
4708
|
+
}
|
|
4709
|
+
},
|
|
4710
|
+
'*'
|
|
4711
|
+
);
|
|
4712
|
+
}
|
|
4713
|
+
});
|
|
5038
4714
|
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
5047
|
-
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
4715
|
+
// PCI Manager for iframe implementation
|
|
4716
|
+
window.PCIManager = {
|
|
4717
|
+
pciInstance: null,
|
|
4718
|
+
container: null,
|
|
4719
|
+
markupEl: null,
|
|
4720
|
+
propertiesEl: null,
|
|
4721
|
+
customInteractionTypeIdentifier: null,
|
|
4722
|
+
responseIdentifier: null,
|
|
4723
|
+
pendingBoundTo: null,
|
|
4724
|
+
pendingMarkup: null,
|
|
4725
|
+
pendingProperties: null,
|
|
4726
|
+
pendingState: null,
|
|
4727
|
+
pendingStylesheets: null,
|
|
4728
|
+
stylesheetKeys: {},
|
|
4729
|
+
interactionChangedViaEvent: false,
|
|
4730
|
+
eventBridgeAttached: false,
|
|
4731
|
+
lastResponseStr: null,
|
|
4732
|
+
hadResponse: false,
|
|
4733
|
+
|
|
4734
|
+
initialize: function (config) {
|
|
4735
|
+
this.customInteractionTypeIdentifier = config.customInteractionTypeIdentifier;
|
|
4736
|
+
this.responseIdentifier = config.responseIdentifier;
|
|
4737
|
+
this.container = document.getElementById('pci-container');
|
|
4738
|
+
this.container.classList.add('qti-customInteraction');
|
|
4739
|
+
|
|
4740
|
+
function qtiVariableHasValue(qtiVar) {
|
|
4741
|
+
if (!qtiVar) return false;
|
|
4742
|
+
if (qtiVar.base) {
|
|
4743
|
+
for (const k in qtiVar.base) {
|
|
4744
|
+
if (!Object.prototype.hasOwnProperty.call(qtiVar.base, k)) continue;
|
|
4745
|
+
const v = qtiVar.base[k];
|
|
4746
|
+
if (v !== null && v !== undefined && v !== '') return true;
|
|
4747
|
+
}
|
|
4748
|
+
}
|
|
4749
|
+
if (qtiVar.list) {
|
|
4750
|
+
for (const k in qtiVar.list) {
|
|
4751
|
+
if (!Object.prototype.hasOwnProperty.call(qtiVar.list, k)) continue;
|
|
4752
|
+
const v = qtiVar.list[k];
|
|
4753
|
+
if (Array.isArray(v) && v.some(x => x !== null && x !== undefined && x !== '')) return true;
|
|
4754
|
+
}
|
|
4755
|
+
}
|
|
4756
|
+
if (Array.isArray(qtiVar.record) && qtiVar.record.length > 0) return true;
|
|
4757
|
+
return false;
|
|
4758
|
+
}
|
|
5052
4759
|
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
|
|
5067
|
-
|
|
5068
|
-
|
|
5069
|
-
|
|
5070
|
-
|
|
5071
|
-
true
|
|
5072
|
-
);
|
|
5073
|
-
}
|
|
4760
|
+
const initialBoundTo = config.boundTo && config.boundTo[this.responseIdentifier];
|
|
4761
|
+
this.hadResponse = qtiVariableHasValue(initialBoundTo);
|
|
4762
|
+
this.lastResponseStr = this.hadResponse ? JSON.stringify(initialBoundTo) : null;
|
|
4763
|
+
// Ensure expected DOM structure exists (markup + properties)
|
|
4764
|
+
this.markupEl = this.container.querySelector('qti-interaction-markup');
|
|
4765
|
+
if (!this.markupEl) {
|
|
4766
|
+
this.markupEl = document.createElement('qti-interaction-markup');
|
|
4767
|
+
this.container.appendChild(this.markupEl);
|
|
4768
|
+
}
|
|
4769
|
+
this.markupEl.classList.add('qti-customInteraction');
|
|
4770
|
+
this.propertiesEl = this.container.querySelector('properties');
|
|
4771
|
+
if (!this.propertiesEl) {
|
|
4772
|
+
this.propertiesEl = document.createElement('properties');
|
|
4773
|
+
this.propertiesEl.style.display = 'none';
|
|
4774
|
+
this.container.appendChild(this.propertiesEl);
|
|
4775
|
+
} else {
|
|
4776
|
+
this.propertiesEl.style.display = 'none';
|
|
4777
|
+
}
|
|
5074
4778
|
|
|
5075
|
-
|
|
5076
|
-
|
|
5077
|
-
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
4779
|
+
// Apply any markup/properties that arrived before initialization
|
|
4780
|
+
if (this.pendingMarkup !== null) {
|
|
4781
|
+
this.setMarkup(this.pendingMarkup);
|
|
4782
|
+
this.pendingMarkup = null;
|
|
4783
|
+
}
|
|
4784
|
+
if (this.pendingProperties !== null) {
|
|
4785
|
+
this.setProperties(this.pendingProperties);
|
|
4786
|
+
this.pendingProperties = null;
|
|
4787
|
+
}
|
|
4788
|
+
if (this.pendingStylesheets !== null) {
|
|
4789
|
+
this.setStylesheets(this.pendingStylesheets);
|
|
4790
|
+
this.pendingStylesheets = null;
|
|
4791
|
+
}
|
|
5082
4792
|
|
|
5083
|
-
|
|
5084
|
-
|
|
5085
|
-
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
4793
|
+
// Bridge qti-interaction-changed events (preferred over polling)
|
|
4794
|
+
if (!this.eventBridgeAttached) {
|
|
4795
|
+
this.eventBridgeAttached = true;
|
|
4796
|
+
const self = this;
|
|
4797
|
+
this.container.addEventListener(
|
|
4798
|
+
'qti-interaction-changed',
|
|
4799
|
+
function (evt) {
|
|
4800
|
+
try {
|
|
4801
|
+
self.interactionChangedViaEvent = true;
|
|
4802
|
+
const value = evt && evt.detail ? evt.detail.value : undefined;
|
|
4803
|
+
if (value !== undefined) {
|
|
4804
|
+
const state =
|
|
4805
|
+
self.pciInstance && typeof self.pciInstance.getState === 'function'
|
|
4806
|
+
? self.pciInstance.getState()
|
|
4807
|
+
: null;
|
|
4808
|
+
self.notifyInteractionChanged(value, state);
|
|
4809
|
+
}
|
|
4810
|
+
} catch (e) {
|
|
4811
|
+
// ignore bridge errors, polling fallback may still work
|
|
4812
|
+
}
|
|
4813
|
+
},
|
|
4814
|
+
true
|
|
4815
|
+
);
|
|
4816
|
+
}
|
|
5090
4817
|
|
|
5091
|
-
|
|
5092
|
-
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
4818
|
+
function getResolvablePath(path, basePath) {
|
|
4819
|
+
if (Array.isArray(path)) {
|
|
4820
|
+
return path.map(p => getResolvablePathString(p, basePath));
|
|
4821
|
+
} else {
|
|
4822
|
+
return getResolvablePathString(path, basePath);
|
|
4823
|
+
}
|
|
4824
|
+
}
|
|
5097
4825
|
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
|
|
5101
|
-
|
|
5102
|
-
|
|
5103
|
-
|
|
5104
|
-
|
|
5105
|
-
const path2Array = Array.isArray(path2) ? path2 : [path2];
|
|
5106
|
-
return path1Array.concat(path2Array).filter((value, index, self) => self.indexOf(value) === index);
|
|
5107
|
-
}
|
|
4826
|
+
function removeDoubleSlashes(str) {
|
|
4827
|
+
return str
|
|
4828
|
+
.replace(/([^:\\/])\\/\\/+/g, '$1/')
|
|
4829
|
+
.replace(/\\/\\//g, '/')
|
|
4830
|
+
.replace('http:/', 'http://')
|
|
4831
|
+
.replace('https:/', 'https://');
|
|
4832
|
+
}
|
|
5108
4833
|
|
|
5109
|
-
// Update paths with modules from the config
|
|
5110
|
-
if (config.interactionModules && config.interactionModules.length > 0) {
|
|
5111
|
-
config.interactionModules.forEach(module => {
|
|
5112
|
-
if (module.id && module.primaryPath) {
|
|
5113
|
-
const currentPath = window.requirePaths[module.id] || [];
|
|
5114
|
-
const currentPaths = Array.isArray(currentPath) ? currentPath : [currentPath];
|
|
5115
|
-
const newPath = combineRequireResolvePaths(
|
|
5116
|
-
module.primaryPath, module.fallbackPath, config.baseUrl
|
|
5117
|
-
);
|
|
5118
|
-
window.requirePaths[module.id] = currentPaths.concat(newPath).filter((value, index, self) => self.indexOf(value) === index);
|
|
5119
|
-
}
|
|
5120
|
-
});
|
|
5121
|
-
}
|
|
5122
4834
|
|
|
5123
|
-
// The ONLY other requirejs.config call - with the context for this specific PCI
|
|
5124
|
-
window.requirejs.config({
|
|
5125
|
-
context: this.customInteractionTypeIdentifier,
|
|
5126
|
-
paths: window.requirePaths,
|
|
5127
|
-
shim: window.requireShim
|
|
5128
|
-
});
|
|
5129
4835
|
|
|
5130
|
-
|
|
5131
|
-
|
|
5132
|
-
|
|
5133
|
-
|
|
5134
|
-
|
|
5135
|
-
// Configure PCI instance
|
|
5136
|
-
const pciConfig = {
|
|
5137
|
-
properties: config.properties || {},
|
|
5138
|
-
contextVariables: config.contextVariables || {},
|
|
5139
|
-
templateVariables: config.templateVariables || {},
|
|
5140
|
-
onready: pciInstance => {
|
|
5141
|
-
this.pciInstance = pciInstance;
|
|
5142
|
-
// Apply any pending updates that arrived before onready
|
|
5143
|
-
if (this.pendingBoundTo) {
|
|
5144
|
-
this.applyBoundTo(this.pendingBoundTo);
|
|
5145
|
-
this.pendingBoundTo = null;
|
|
4836
|
+
function combineRequireResolvePaths(path1, path2, baseUrl) {
|
|
4837
|
+
path1 = getResolvablePath(path1, baseUrl);
|
|
4838
|
+
const path1Array = Array.isArray(path1) ? path1 : [path1];
|
|
4839
|
+
if (!path2) {
|
|
4840
|
+
return path1Array;
|
|
5146
4841
|
}
|
|
5147
|
-
|
|
5148
|
-
|
|
5149
|
-
|
|
5150
|
-
|
|
5151
|
-
|
|
5152
|
-
|
|
5153
|
-
|
|
5154
|
-
|
|
5155
|
-
|
|
5156
|
-
|
|
5157
|
-
|
|
5158
|
-
|
|
5159
|
-
|
|
5160
|
-
|
|
5161
|
-
|
|
5162
|
-
|
|
5163
|
-
|
|
5164
|
-
|
|
5165
|
-
|
|
5166
|
-
|
|
5167
|
-
|
|
5168
|
-
|
|
5169
|
-
|
|
5170
|
-
|
|
5171
|
-
|
|
5172
|
-
|
|
5173
|
-
|
|
5174
|
-
|
|
5175
|
-
|
|
5176
|
-
|
|
5177
|
-
|
|
5178
|
-
|
|
5179
|
-
|
|
5180
|
-
|
|
5181
|
-
|
|
5182
|
-
|
|
5183
|
-
|
|
5184
|
-
|
|
5185
|
-
|
|
5186
|
-
}
|
|
5187
|
-
|
|
5188
|
-
|
|
5189
|
-
|
|
5190
|
-
|
|
5191
|
-
|
|
5192
|
-
|
|
5193
|
-
|
|
5194
|
-
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
4842
|
+
path2 = getResolvablePath(path2, baseUrl);
|
|
4843
|
+
const path2Array = Array.isArray(path2) ? path2 : [path2];
|
|
4844
|
+
return path1Array.concat(path2Array).filter((value, index, self) => self.indexOf(value) === index);
|
|
4845
|
+
}
|
|
4846
|
+
|
|
4847
|
+
// Update paths with modules from the config
|
|
4848
|
+
if (config.interactionModules && config.interactionModules.length > 0) {
|
|
4849
|
+
config.interactionModules.forEach(module => {
|
|
4850
|
+
if (module.id && module.primaryPath) {
|
|
4851
|
+
const currentPath = window.requirePaths[module.id] || [];
|
|
4852
|
+
const currentPaths = Array.isArray(currentPath) ? currentPath : [currentPath];
|
|
4853
|
+
const newPath = combineRequireResolvePaths(
|
|
4854
|
+
module.primaryPath,
|
|
4855
|
+
module.fallbackPath,
|
|
4856
|
+
config.baseUrl
|
|
4857
|
+
);
|
|
4858
|
+
window.requirePaths[module.id] = currentPaths
|
|
4859
|
+
.concat(newPath)
|
|
4860
|
+
.filter((value, index, self) => self.indexOf(value) === index);
|
|
4861
|
+
}
|
|
4862
|
+
});
|
|
4863
|
+
}
|
|
4864
|
+
|
|
4865
|
+
// The ONLY other requirejs.config call - with the context for this specific PCI
|
|
4866
|
+
window.requirejs.config({
|
|
4867
|
+
context: this.customInteractionTypeIdentifier,
|
|
4868
|
+
paths: window.requirePaths,
|
|
4869
|
+
shim: window.requireShim
|
|
4870
|
+
});
|
|
4871
|
+
|
|
4872
|
+
// Define qtiCustomInteractionContext for the PCI
|
|
4873
|
+
define('qtiCustomInteractionContext', () => {
|
|
4874
|
+
return {
|
|
4875
|
+
register: pciInstance => {
|
|
4876
|
+
this.pciInstance = pciInstance;
|
|
4877
|
+
// Configure PCI instance
|
|
4878
|
+
const pciConfig = {
|
|
4879
|
+
properties: config.properties || {},
|
|
4880
|
+
contextVariables: config.contextVariables || {},
|
|
4881
|
+
templateVariables: config.templateVariables || {},
|
|
4882
|
+
onready: pciInstance => {
|
|
4883
|
+
this.pciInstance = pciInstance;
|
|
4884
|
+
// Apply any pending updates that arrived before onready
|
|
4885
|
+
if (this.pendingBoundTo) {
|
|
4886
|
+
this.applyBoundTo(this.pendingBoundTo);
|
|
4887
|
+
this.pendingBoundTo = null;
|
|
4888
|
+
}
|
|
4889
|
+
if (this.pendingState && typeof this.pciInstance.setState === 'function') {
|
|
4890
|
+
this.pciInstance.setState(this.pendingState);
|
|
4891
|
+
this.pendingState = null;
|
|
4892
|
+
}
|
|
4893
|
+
this.notifyReady();
|
|
4894
|
+
},
|
|
4895
|
+
ondone: (pciInstance, response, state, status) => {
|
|
4896
|
+
this.notifyInteractionChanged(response, typeof state === 'string' ? state : null);
|
|
4897
|
+
},
|
|
4898
|
+
responseIdentifier: config.responseIdentifier,
|
|
4899
|
+
boundTo: config.boundTo
|
|
4900
|
+
};
|
|
4901
|
+
|
|
4902
|
+
if (pciInstance.getInstance) {
|
|
4903
|
+
const dom = this.markupEl || this.container;
|
|
4904
|
+
// Round-trip support for object states (stored as a prefixed JSON string by the host).
|
|
4905
|
+
// For strict string-based PCIs we pass the original string through unchanged.
|
|
4906
|
+
let restoredState = config.state;
|
|
4907
|
+
if (typeof restoredState === 'string' && restoredState.indexOf('__qti_json__::') === 0) {
|
|
4908
|
+
try {
|
|
4909
|
+
restoredState = JSON.parse(restoredState.substring('__qti_json__::'.length));
|
|
4910
|
+
} catch (e) {
|
|
4911
|
+
// If parsing fails, fall back to the raw string.
|
|
4912
|
+
restoredState = config.state;
|
|
4913
|
+
}
|
|
5199
4914
|
}
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
5211
|
-
|
|
5212
|
-
|
|
5213
|
-
|
|
5214
|
-
|
|
5215
|
-
|
|
4915
|
+
pciInstance.getInstance(dom, pciConfig, restoredState || undefined);
|
|
4916
|
+
} else {
|
|
4917
|
+
// TAO custom interaction initialization
|
|
4918
|
+
const restoreTAOConfig = dataset => {
|
|
4919
|
+
const config = {};
|
|
4920
|
+
const parseDataAttributes = () => {
|
|
4921
|
+
const result = {};
|
|
4922
|
+
|
|
4923
|
+
// Separate direct attributes from nested ones
|
|
4924
|
+
Object.entries(dataset || {}).forEach(([key, value]) => {
|
|
4925
|
+
if (!key.includes('__')) {
|
|
4926
|
+
// Direct attributes (like version)
|
|
4927
|
+
result[key] = value;
|
|
4928
|
+
}
|
|
4929
|
+
});
|
|
4930
|
+
|
|
4931
|
+
// Parse nested attributes
|
|
4932
|
+
const nestedData = {};
|
|
4933
|
+
|
|
4934
|
+
Object.entries(dataset || {}).forEach(([key, value]) => {
|
|
4935
|
+
const parts = key.split('__');
|
|
4936
|
+
if (parts.length > 1) {
|
|
4937
|
+
const [group, index, prop] = parts;
|
|
4938
|
+
nestedData[group] = nestedData[group] || {};
|
|
4939
|
+
nestedData[group][index] = nestedData[group][index] || {};
|
|
4940
|
+
nestedData[group][index][prop] = value;
|
|
4941
|
+
}
|
|
4942
|
+
});
|
|
4943
|
+
|
|
4944
|
+
// Convert nested groups to arrays
|
|
4945
|
+
Object.entries(nestedData).forEach(([key, group]) => {
|
|
4946
|
+
result[key] = Object.values(group);
|
|
4947
|
+
});
|
|
4948
|
+
return result;
|
|
4949
|
+
};
|
|
4950
|
+
const data = parseDataAttributes();
|
|
4951
|
+
for (const key in data) {
|
|
4952
|
+
if (Object.prototype.hasOwnProperty.call(data, key)) {
|
|
4953
|
+
const value = data[key];
|
|
4954
|
+
if (key === 'config') {
|
|
4955
|
+
config[key] = JSON.parse(value);
|
|
4956
|
+
} else {
|
|
4957
|
+
config[key] = value;
|
|
4958
|
+
}
|
|
4959
|
+
}
|
|
4960
|
+
}
|
|
4961
|
+
return config;
|
|
4962
|
+
};
|
|
4963
|
+
const taoConfig = restoreTAOConfig(config.dataAttributes);
|
|
4964
|
+
|
|
4965
|
+
this.pciInstance.initialize(
|
|
4966
|
+
this.customInteractionTypeIdentifier,
|
|
4967
|
+
(this.markupEl || this.container).firstElementChild || this.markupEl || this.container,
|
|
4968
|
+
Object.keys(taoConfig).length ? taoConfig : null
|
|
4969
|
+
);
|
|
4970
|
+
}
|
|
4971
|
+
},
|
|
4972
|
+
notifyReady: () => {
|
|
4973
|
+
PCIManager.notifyReady();
|
|
5216
4974
|
}
|
|
5217
|
-
}
|
|
4975
|
+
};
|
|
4976
|
+
});
|
|
4977
|
+
|
|
4978
|
+
function getResolvablePathString(path, basePath) {
|
|
4979
|
+
path = path.replace(/\\.js$/, '');
|
|
4980
|
+
return path?.toLocaleLowerCase().startsWith('http') || !basePath
|
|
4981
|
+
? path
|
|
4982
|
+
: removeDoubleSlashes(\`\${basePath}/\${path}\`);
|
|
5218
4983
|
}
|
|
5219
|
-
return config;
|
|
5220
|
-
};
|
|
5221
|
-
const taoConfig = restoreTAOConfig(config.dataAttributes);
|
|
5222
4984
|
|
|
5223
|
-
|
|
5224
|
-
this.
|
|
5225
|
-
|
|
5226
|
-
Object.keys(taoConfig).length ? taoConfig : null
|
|
5227
|
-
);
|
|
5228
|
-
}
|
|
5229
|
-
},
|
|
5230
|
-
notifyReady: () => {
|
|
5231
|
-
PCIManager.notifyReady();
|
|
5232
|
-
}
|
|
5233
|
-
};
|
|
5234
|
-
});
|
|
4985
|
+
// Load the PCI module
|
|
4986
|
+
this.loadModule(config.module);
|
|
4987
|
+
},
|
|
5235
4988
|
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
4989
|
+
loadModule: function (modulePath) {
|
|
4990
|
+
try {
|
|
4991
|
+
// Get the context-specific require
|
|
4992
|
+
const contextRequire = window.requirejs.config({
|
|
4993
|
+
context: this.customInteractionTypeIdentifier
|
|
4994
|
+
});
|
|
4995
|
+
contextRequire(['require'], require => {
|
|
4996
|
+
// Now load the actual module
|
|
4997
|
+
require([modulePath], () => {}, err => {
|
|
4998
|
+
console.error('Error loading module:', modulePath, err);
|
|
4999
|
+
this.notifyError('Module load error: ' + err.toString());
|
|
5000
|
+
});
|
|
5001
|
+
});
|
|
5002
|
+
} catch (error) {
|
|
5003
|
+
console.error('Exception in loadModule:', modulePath);
|
|
5004
|
+
console.error(error);
|
|
5005
|
+
this.notifyError('Error in require call: ' + error.toString());
|
|
5006
|
+
}
|
|
5007
|
+
},
|
|
5239
5008
|
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
|
|
5245
|
-
|
|
5246
|
-
|
|
5247
|
-
|
|
5248
|
-
|
|
5249
|
-
},
|
|
5250
|
-
console.error('Error loading module:', modulePath, err);
|
|
5251
|
-
this.notifyError('Module load error: ' + err.toString());
|
|
5252
|
-
});
|
|
5253
|
-
});
|
|
5254
|
-
} catch (error) {
|
|
5255
|
-
console.error('Exception in loadModule:', modulePath);
|
|
5256
|
-
console.error(error);
|
|
5257
|
-
this.notifyError('Error in require call: ' + error.toString());
|
|
5258
|
-
}
|
|
5259
|
-
},
|
|
5009
|
+
notifyReady: function () {
|
|
5010
|
+
window.parent.postMessage(
|
|
5011
|
+
{
|
|
5012
|
+
source: 'qti-pci-iframe',
|
|
5013
|
+
responseIdentifier: this.responseIdentifier,
|
|
5014
|
+
method: 'iframeReady'
|
|
5015
|
+
},
|
|
5016
|
+
'*'
|
|
5017
|
+
);
|
|
5018
|
+
},
|
|
5260
5019
|
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
|
|
5268
|
-
|
|
5269
|
-
|
|
5270
|
-
|
|
5271
|
-
|
|
5272
|
-
responseIdentifier: this.responseIdentifier,
|
|
5273
|
-
method: 'interactionChanged',
|
|
5274
|
-
params: { value: response, state: state }
|
|
5275
|
-
}, '*');
|
|
5276
|
-
},
|
|
5277
|
-
|
|
5278
|
-
notifyError: function(message) {
|
|
5279
|
-
console.error('PCI Error:', message);
|
|
5280
|
-
window.parent.postMessage({
|
|
5281
|
-
source: 'qti-pci-iframe',
|
|
5282
|
-
responseIdentifier: this.responseIdentifier,
|
|
5283
|
-
method: 'error',
|
|
5284
|
-
params: { message: message }
|
|
5285
|
-
}, '*');
|
|
5286
|
-
},
|
|
5287
|
-
|
|
5288
|
-
setMarkup: function(markupHtml) {
|
|
5289
|
-
if (!this.container) {
|
|
5290
|
-
this.container = document.getElementById('pci-container');
|
|
5291
|
-
}
|
|
5292
|
-
if (!this.container) {
|
|
5293
|
-
this.pendingMarkup = markupHtml;
|
|
5294
|
-
return;
|
|
5295
|
-
}
|
|
5296
|
-
this.markupEl = this.container.querySelector('qti-interaction-markup');
|
|
5297
|
-
if (!this.markupEl) {
|
|
5298
|
-
this.markupEl = document.createElement('qti-interaction-markup');
|
|
5299
|
-
this.container.appendChild(this.markupEl);
|
|
5300
|
-
}
|
|
5301
|
-
this.markupEl.classList.add('qti-customInteraction');
|
|
5302
|
-
this.markupEl.innerHTML = markupHtml || '';
|
|
5303
|
-
},
|
|
5020
|
+
notifyInteractionChanged: function (response, state) {
|
|
5021
|
+
window.parent.postMessage(
|
|
5022
|
+
{
|
|
5023
|
+
source: 'qti-pci-iframe',
|
|
5024
|
+
responseIdentifier: this.responseIdentifier,
|
|
5025
|
+
method: 'interactionChanged',
|
|
5026
|
+
params: { value: response, state: state }
|
|
5027
|
+
},
|
|
5028
|
+
'*'
|
|
5029
|
+
);
|
|
5030
|
+
},
|
|
5304
5031
|
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
|
|
5313
|
-
|
|
5314
|
-
|
|
5315
|
-
|
|
5316
|
-
|
|
5317
|
-
}
|
|
5318
|
-
this.propertiesEl.style.display = 'none';
|
|
5319
|
-
this.propertiesEl.innerHTML = propertiesHtml || '';
|
|
5320
|
-
},
|
|
5032
|
+
notifyError: function (message) {
|
|
5033
|
+
console.error('PCI Error:', message);
|
|
5034
|
+
window.parent.postMessage(
|
|
5035
|
+
{
|
|
5036
|
+
source: 'qti-pci-iframe',
|
|
5037
|
+
responseIdentifier: this.responseIdentifier,
|
|
5038
|
+
method: 'error',
|
|
5039
|
+
params: { message: message }
|
|
5040
|
+
},
|
|
5041
|
+
'*'
|
|
5042
|
+
);
|
|
5043
|
+
},
|
|
5321
5044
|
|
|
5322
|
-
|
|
5323
|
-
|
|
5324
|
-
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5045
|
+
setMarkup: function (markupHtml) {
|
|
5046
|
+
if (!this.container) {
|
|
5047
|
+
this.container = document.getElementById('pci-container');
|
|
5048
|
+
}
|
|
5049
|
+
if (!this.container) {
|
|
5050
|
+
this.pendingMarkup = markupHtml;
|
|
5051
|
+
return;
|
|
5052
|
+
}
|
|
5053
|
+
this.markupEl = this.container.querySelector('qti-interaction-markup');
|
|
5054
|
+
if (!this.markupEl) {
|
|
5055
|
+
this.markupEl = document.createElement('qti-interaction-markup');
|
|
5056
|
+
this.container.appendChild(this.markupEl);
|
|
5057
|
+
}
|
|
5058
|
+
this.markupEl.classList.add('qti-customInteraction');
|
|
5059
|
+
this.markupEl.innerHTML = markupHtml || '';
|
|
5060
|
+
},
|
|
5329
5061
|
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
5340
|
-
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5062
|
+
setProperties: function (propertiesHtml) {
|
|
5063
|
+
if (!this.container) {
|
|
5064
|
+
this.container = document.getElementById('pci-container');
|
|
5065
|
+
}
|
|
5066
|
+
if (!this.container) {
|
|
5067
|
+
this.pendingProperties = propertiesHtml;
|
|
5068
|
+
return;
|
|
5069
|
+
}
|
|
5070
|
+
this.propertiesEl = this.container.querySelector('properties');
|
|
5071
|
+
if (!this.propertiesEl) {
|
|
5072
|
+
this.propertiesEl = document.createElement('properties');
|
|
5073
|
+
this.container.appendChild(this.propertiesEl);
|
|
5074
|
+
}
|
|
5075
|
+
this.propertiesEl.style.display = 'none';
|
|
5076
|
+
this.propertiesEl.innerHTML = propertiesHtml || '';
|
|
5077
|
+
},
|
|
5345
5078
|
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
|
|
5349
|
-
|
|
5350
|
-
|
|
5351
|
-
|
|
5352
|
-
|
|
5353
|
-
this.injectStylesheet(sheet.content, key, scoped);
|
|
5354
|
-
return;
|
|
5355
|
-
}
|
|
5356
|
-
if (sheet.href) {
|
|
5357
|
-
fetch(sheet.href)
|
|
5358
|
-
.then(resp => resp.text())
|
|
5359
|
-
.then(css => this.injectStylesheet(css, key, scoped))
|
|
5360
|
-
.catch(() => {
|
|
5361
|
-
// ignore stylesheet load errors
|
|
5362
|
-
});
|
|
5363
|
-
}
|
|
5364
|
-
});
|
|
5365
|
-
},
|
|
5079
|
+
minifyCss: function (cssContent) {
|
|
5080
|
+
return cssContent
|
|
5081
|
+
.replace(/\\/\\*[\\s\\S]*?\\*\\//g, '')
|
|
5082
|
+
.replace(/\\s+/g, ' ')
|
|
5083
|
+
.replace(/\\s*([{}:;])\\s*/g, '$1')
|
|
5084
|
+
.trim();
|
|
5085
|
+
},
|
|
5366
5086
|
|
|
5367
|
-
|
|
5368
|
-
|
|
5369
|
-
|
|
5370
|
-
|
|
5371
|
-
|
|
5372
|
-
|
|
5087
|
+
setStylesheets: function (stylesheetsHtml) {
|
|
5088
|
+
if (!stylesheetsHtml) return;
|
|
5089
|
+
if (!this.container) {
|
|
5090
|
+
this.container = document.getElementById('pci-container');
|
|
5091
|
+
}
|
|
5092
|
+
const target = this.container || document.body;
|
|
5093
|
+
if (!target) return;
|
|
5094
|
+
target.insertAdjacentHTML('afterbegin', stylesheetsHtml);
|
|
5095
|
+
},
|
|
5373
5096
|
|
|
5374
|
-
// Set up message listener for communication with parent
|
|
5375
|
-
let expectedParentOrigin = null;
|
|
5376
|
-
window.addEventListener('message', function(event) {
|
|
5377
|
-
const { data } = event;
|
|
5378
|
-
|
|
5379
|
-
// Ensure the message is from our parent
|
|
5380
|
-
if (event.source !== window.parent || !data || data.source !== 'qti-portable-custom-interaction') {
|
|
5381
|
-
return;
|
|
5382
|
-
}
|
|
5383
|
-
if (expectedParentOrigin === null) {
|
|
5384
|
-
expectedParentOrigin = event.origin;
|
|
5385
|
-
} else if (event.origin !== expectedParentOrigin) {
|
|
5386
|
-
return;
|
|
5387
|
-
}
|
|
5388
|
-
|
|
5389
|
-
function deepQuerySelector(root, selector) {
|
|
5390
|
-
if (!root) return null;
|
|
5391
|
-
try {
|
|
5392
|
-
const direct = root.querySelector ? root.querySelector(selector) : null;
|
|
5393
|
-
if (direct) return direct;
|
|
5394
|
-
} catch (e) {
|
|
5395
|
-
// ignore invalid selector for this root
|
|
5396
|
-
}
|
|
5397
|
-
if (!root.querySelectorAll) return null;
|
|
5398
|
-
const nodes = root.querySelectorAll('*');
|
|
5399
|
-
for (const node of nodes) {
|
|
5400
|
-
if (node && node.shadowRoot) {
|
|
5401
|
-
const found = deepQuerySelector(node.shadowRoot, selector);
|
|
5402
|
-
if (found) return found;
|
|
5403
|
-
}
|
|
5404
|
-
}
|
|
5405
|
-
return null;
|
|
5406
|
-
}
|
|
5407
5097
|
|
|
5408
|
-
|
|
5409
|
-
|
|
5410
|
-
|
|
5411
|
-
|
|
5412
|
-
|
|
5413
|
-
|
|
5414
|
-
if (node.shadowRoot) {
|
|
5415
|
-
const found = deepFindElementByExactText(node.shadowRoot, text);
|
|
5416
|
-
if (found) return found;
|
|
5417
|
-
}
|
|
5418
|
-
}
|
|
5419
|
-
}
|
|
5420
|
-
return null;
|
|
5421
|
-
}
|
|
5098
|
+
applyBoundTo: function (boundTo) {
|
|
5099
|
+
if (!this.pciInstance || typeof this.pciInstance.setResponse !== 'function') return;
|
|
5100
|
+
const value = boundTo && (boundTo[this.responseIdentifier] || boundTo[Object.keys(boundTo)[0]]);
|
|
5101
|
+
if (value) this.pciInstance.setResponse(value);
|
|
5102
|
+
}
|
|
5103
|
+
};
|
|
5422
5104
|
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
5426
|
-
|
|
5105
|
+
// Set up message listener for communication with parent
|
|
5106
|
+
let expectedParentOrigin = null;
|
|
5107
|
+
window.addEventListener('message', function (event) {
|
|
5108
|
+
const { data } = event;
|
|
5427
5109
|
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5110
|
+
// Ensure the message is from our parent
|
|
5111
|
+
if (event.source !== window.parent || !data || data.source !== 'qti-portable-custom-interaction') {
|
|
5112
|
+
return;
|
|
5113
|
+
}
|
|
5114
|
+
if (expectedParentOrigin === null) {
|
|
5115
|
+
expectedParentOrigin = event.origin;
|
|
5116
|
+
} else if (event.origin !== expectedParentOrigin) {
|
|
5117
|
+
return;
|
|
5118
|
+
}
|
|
5431
5119
|
|
|
5432
|
-
|
|
5433
|
-
|
|
5434
|
-
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
5438
|
-
|
|
5120
|
+
function deepQuerySelector(root, selector) {
|
|
5121
|
+
if (!root) return null;
|
|
5122
|
+
try {
|
|
5123
|
+
const direct = root.querySelector ? root.querySelector(selector) : null;
|
|
5124
|
+
if (direct) return direct;
|
|
5125
|
+
} catch (e) {
|
|
5126
|
+
// ignore invalid selector for this root
|
|
5127
|
+
}
|
|
5128
|
+
if (!root.querySelectorAll) return null;
|
|
5129
|
+
const nodes = root.querySelectorAll('*');
|
|
5130
|
+
for (const node of nodes) {
|
|
5131
|
+
if (node && node.shadowRoot) {
|
|
5132
|
+
const found = deepQuerySelector(node.shadowRoot, selector);
|
|
5133
|
+
if (found) return found;
|
|
5134
|
+
}
|
|
5135
|
+
}
|
|
5136
|
+
return null;
|
|
5137
|
+
}
|
|
5439
5138
|
|
|
5440
|
-
|
|
5441
|
-
|
|
5442
|
-
|
|
5139
|
+
function deepFindElementByExactText(root, text) {
|
|
5140
|
+
if (!root || !text) return null;
|
|
5141
|
+
if (root.querySelectorAll) {
|
|
5142
|
+
const nodes = root.querySelectorAll('*');
|
|
5143
|
+
for (const node of nodes) {
|
|
5144
|
+
if ((node.textContent || '').trim() === text) return node;
|
|
5145
|
+
if (node.shadowRoot) {
|
|
5146
|
+
const found = deepFindElementByExactText(node.shadowRoot, text);
|
|
5147
|
+
if (found) return found;
|
|
5148
|
+
}
|
|
5149
|
+
}
|
|
5150
|
+
}
|
|
5151
|
+
return null;
|
|
5152
|
+
}
|
|
5443
5153
|
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
|
|
5154
|
+
switch (data.method) {
|
|
5155
|
+
case 'initialize':
|
|
5156
|
+
PCIManager.initialize(data.params);
|
|
5157
|
+
break;
|
|
5447
5158
|
|
|
5448
|
-
|
|
5449
|
-
|
|
5450
|
-
|
|
5451
|
-
} else {
|
|
5452
|
-
PCIManager.pendingState = (data.params && data.params.state) || data.params;
|
|
5453
|
-
}
|
|
5454
|
-
break;
|
|
5159
|
+
case 'setMarkup':
|
|
5160
|
+
PCIManager.setMarkup(data.params);
|
|
5161
|
+
break;
|
|
5455
5162
|
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
|
|
5459
|
-
|
|
5460
|
-
|
|
5461
|
-
|
|
5462
|
-
|
|
5463
|
-
if (node && node.shadowRoot) {
|
|
5464
|
-
parts.push(node.shadowRoot.innerHTML || '');
|
|
5465
|
-
parts.push(...collectShadowHtml(node.shadowRoot));
|
|
5466
|
-
}
|
|
5467
|
-
}
|
|
5468
|
-
return parts;
|
|
5469
|
-
};
|
|
5470
|
-
const shadowHtml = collectShadowHtml(document).join('\\n');
|
|
5471
|
-
window.parent.postMessage(
|
|
5472
|
-
{
|
|
5473
|
-
source: 'qti-pci-iframe',
|
|
5474
|
-
responseIdentifier: PCIManager.responseIdentifier,
|
|
5475
|
-
method: 'getContentResponse',
|
|
5476
|
-
messageId: messageId,
|
|
5477
|
-
content: (document.documentElement ? document.documentElement.outerHTML : '') + '\\n' + shadowHtml
|
|
5478
|
-
},
|
|
5479
|
-
'*'
|
|
5480
|
-
);
|
|
5481
|
-
break;
|
|
5482
|
-
}
|
|
5163
|
+
case 'setBoundTo':
|
|
5164
|
+
if (PCIManager.pciInstance) {
|
|
5165
|
+
PCIManager.applyBoundTo(data.params);
|
|
5166
|
+
} else {
|
|
5167
|
+
PCIManager.pendingBoundTo = data.params;
|
|
5168
|
+
}
|
|
5169
|
+
break;
|
|
5483
5170
|
|
|
5484
|
-
|
|
5485
|
-
|
|
5486
|
-
|
|
5487
|
-
|
|
5488
|
-
|
|
5489
|
-
|
|
5490
|
-
|
|
5491
|
-
|
|
5492
|
-
|
|
5493
|
-
|
|
5494
|
-
|
|
5495
|
-
|
|
5496
|
-
|
|
5497
|
-
|
|
5498
|
-
|
|
5499
|
-
|
|
5500
|
-
|
|
5501
|
-
|
|
5502
|
-
|
|
5503
|
-
|
|
5504
|
-
|
|
5505
|
-
|
|
5506
|
-
|
|
5507
|
-
|
|
5508
|
-
|
|
5509
|
-
|
|
5510
|
-
|
|
5511
|
-
}
|
|
5512
|
-
|
|
5513
|
-
case 'getBoundingRect': {
|
|
5514
|
-
const messageId = data.params && data.params.messageId;
|
|
5515
|
-
const selector = data.params && data.params.selector;
|
|
5516
|
-
const el = selector ? deepQuerySelector(document, selector) : null;
|
|
5517
|
-
const rect = el && typeof el.getBoundingClientRect === 'function' ? el.getBoundingClientRect() : null;
|
|
5518
|
-
window.parent.postMessage(
|
|
5519
|
-
{
|
|
5520
|
-
source: 'qti-pci-iframe',
|
|
5521
|
-
responseIdentifier: PCIManager.responseIdentifier,
|
|
5522
|
-
method: 'getBoundingRectResponse',
|
|
5523
|
-
messageId: messageId,
|
|
5524
|
-
rect: rect
|
|
5525
|
-
? {
|
|
5526
|
-
left: rect.left,
|
|
5527
|
-
top: rect.top,
|
|
5528
|
-
width: rect.width,
|
|
5529
|
-
height: rect.height
|
|
5171
|
+
case 'setProperties':
|
|
5172
|
+
PCIManager.setProperties(data.params);
|
|
5173
|
+
break;
|
|
5174
|
+
|
|
5175
|
+
case 'setStylesheets':
|
|
5176
|
+
PCIManager.setStylesheets(data.params);
|
|
5177
|
+
break;
|
|
5178
|
+
|
|
5179
|
+
case 'setState':
|
|
5180
|
+
if (PCIManager.pciInstance && typeof PCIManager.pciInstance.setState === 'function') {
|
|
5181
|
+
PCIManager.pciInstance.setState((data.params && data.params.state) || data.params);
|
|
5182
|
+
} else {
|
|
5183
|
+
PCIManager.pendingState = (data.params && data.params.state) || data.params;
|
|
5184
|
+
}
|
|
5185
|
+
break;
|
|
5186
|
+
|
|
5187
|
+
case 'getContent': {
|
|
5188
|
+
const messageId = data.params && data.params.messageId;
|
|
5189
|
+
const collectShadowHtml = root => {
|
|
5190
|
+
const parts = [];
|
|
5191
|
+
if (!root || !root.querySelectorAll) return parts;
|
|
5192
|
+
const nodes = root.querySelectorAll('*');
|
|
5193
|
+
for (const node of nodes) {
|
|
5194
|
+
if (node && node.shadowRoot) {
|
|
5195
|
+
parts.push(node.shadowRoot.innerHTML || '');
|
|
5196
|
+
parts.push(...collectShadowHtml(node.shadowRoot));
|
|
5197
|
+
}
|
|
5530
5198
|
}
|
|
5531
|
-
|
|
5532
|
-
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5199
|
+
return parts;
|
|
5200
|
+
};
|
|
5201
|
+
const shadowHtml = collectShadowHtml(document).join('\\n');
|
|
5202
|
+
window.parent.postMessage(
|
|
5203
|
+
{
|
|
5204
|
+
source: 'qti-pci-iframe',
|
|
5205
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
5206
|
+
method: 'getContentResponse',
|
|
5207
|
+
messageId: messageId,
|
|
5208
|
+
content: (document.documentElement ? document.documentElement.outerHTML : '') + '\\n' + shadowHtml
|
|
5209
|
+
},
|
|
5210
|
+
'*'
|
|
5211
|
+
);
|
|
5212
|
+
break;
|
|
5213
|
+
}
|
|
5537
5214
|
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
|
|
5562
|
-
|
|
5563
|
-
|
|
5564
|
-
|
|
5565
|
-
|
|
5566
|
-
responseIdentifier: PCIManager.responseIdentifier,
|
|
5567
|
-
method: 'clickTextResponse',
|
|
5568
|
-
messageId: messageId,
|
|
5569
|
-
success: success
|
|
5570
|
-
},
|
|
5571
|
-
'*'
|
|
5572
|
-
);
|
|
5573
|
-
break;
|
|
5574
|
-
}
|
|
5575
|
-
|
|
5576
|
-
case 'setValueElement': {
|
|
5577
|
-
const messageId = data.params && data.params.messageId;
|
|
5578
|
-
const selector = data.params && data.params.selector;
|
|
5579
|
-
const value = data.params && data.params.value;
|
|
5580
|
-
const el = selector ? deepQuerySelector(document, selector) : null;
|
|
5581
|
-
let success = false;
|
|
5582
|
-
if (el && 'value' in el) {
|
|
5583
|
-
try {
|
|
5584
|
-
el.value = value;
|
|
5585
|
-
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
5586
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
5587
|
-
success = true;
|
|
5588
|
-
} catch (e) {
|
|
5589
|
-
success = false;
|
|
5590
|
-
}
|
|
5591
|
-
}
|
|
5592
|
-
window.parent.postMessage(
|
|
5593
|
-
{
|
|
5594
|
-
source: 'qti-pci-iframe',
|
|
5595
|
-
responseIdentifier: PCIManager.responseIdentifier,
|
|
5596
|
-
method: 'setValueResponse',
|
|
5597
|
-
messageId: messageId,
|
|
5598
|
-
success: success
|
|
5599
|
-
},
|
|
5600
|
-
'*'
|
|
5601
|
-
);
|
|
5602
|
-
break;
|
|
5603
|
-
}
|
|
5604
|
-
|
|
5605
|
-
case 'console':
|
|
5606
|
-
window.parent.postMessage(
|
|
5607
|
-
{
|
|
5608
|
-
source: 'qti-pci-iframe',
|
|
5609
|
-
responseIdentifier: PCIManager.responseIdentifier,
|
|
5610
|
-
method: 'console',
|
|
5611
|
-
level: data.level,
|
|
5612
|
-
args: data.args
|
|
5613
|
-
},
|
|
5614
|
-
'*'
|
|
5615
|
-
);
|
|
5616
|
-
break;
|
|
5617
|
-
}
|
|
5618
|
-
});
|
|
5215
|
+
case 'simulateClick': {
|
|
5216
|
+
const messageId = data.params && data.params.messageId;
|
|
5217
|
+
const x = data.params && data.params.x;
|
|
5218
|
+
const y = data.params && data.params.y;
|
|
5219
|
+
const el = typeof x === 'number' && typeof y === 'number' ? document.elementFromPoint(x, y) : null;
|
|
5220
|
+
const target = (el && el.closest && el.closest('.hitbox')) || document.querySelector('.hitbox') || el;
|
|
5221
|
+
if (target) {
|
|
5222
|
+
const evt = new MouseEvent('click', {
|
|
5223
|
+
bubbles: true,
|
|
5224
|
+
clientX: x,
|
|
5225
|
+
clientY: y,
|
|
5226
|
+
screenX: x,
|
|
5227
|
+
screenY: y,
|
|
5228
|
+
view: window
|
|
5229
|
+
});
|
|
5230
|
+
target.dispatchEvent(evt);
|
|
5231
|
+
}
|
|
5232
|
+
window.parent.postMessage(
|
|
5233
|
+
{
|
|
5234
|
+
source: 'qti-pci-iframe',
|
|
5235
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
5236
|
+
method: 'clickResponse',
|
|
5237
|
+
messageId: messageId
|
|
5238
|
+
},
|
|
5239
|
+
'*'
|
|
5240
|
+
);
|
|
5241
|
+
break;
|
|
5242
|
+
}
|
|
5619
5243
|
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
5634
|
-
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5244
|
+
case 'getBoundingRect': {
|
|
5245
|
+
const messageId = data.params && data.params.messageId;
|
|
5246
|
+
const selector = data.params && data.params.selector;
|
|
5247
|
+
const el = selector ? deepQuerySelector(document, selector) : null;
|
|
5248
|
+
const rect = el && typeof el.getBoundingClientRect === 'function' ? el.getBoundingClientRect() : null;
|
|
5249
|
+
window.parent.postMessage(
|
|
5250
|
+
{
|
|
5251
|
+
source: 'qti-pci-iframe',
|
|
5252
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
5253
|
+
method: 'getBoundingRectResponse',
|
|
5254
|
+
messageId: messageId,
|
|
5255
|
+
rect: rect
|
|
5256
|
+
? {
|
|
5257
|
+
left: rect.left,
|
|
5258
|
+
top: rect.top,
|
|
5259
|
+
width: rect.width,
|
|
5260
|
+
height: rect.height
|
|
5261
|
+
}
|
|
5262
|
+
: null
|
|
5263
|
+
},
|
|
5264
|
+
'*'
|
|
5265
|
+
);
|
|
5266
|
+
break;
|
|
5267
|
+
}
|
|
5639
5268
|
|
|
5640
|
-
|
|
5641
|
-
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
5645
|
-
|
|
5269
|
+
case 'clickOnSelector': {
|
|
5270
|
+
const messageId = data.params && data.params.messageId;
|
|
5271
|
+
const selector = data.params && data.params.selector;
|
|
5272
|
+
const el = selector ? deepQuerySelector(document, selector) : null;
|
|
5273
|
+
const success = !!el;
|
|
5274
|
+
if (el && typeof el.click === 'function') el.click();
|
|
5275
|
+
window.parent.postMessage(
|
|
5276
|
+
{
|
|
5277
|
+
source: 'qti-pci-iframe',
|
|
5278
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
5279
|
+
method: 'clickSelectorResponse',
|
|
5280
|
+
messageId: messageId,
|
|
5281
|
+
success: success
|
|
5282
|
+
},
|
|
5283
|
+
'*'
|
|
5284
|
+
);
|
|
5285
|
+
break;
|
|
5286
|
+
}
|
|
5646
5287
|
|
|
5647
|
-
|
|
5648
|
-
|
|
5649
|
-
|
|
5288
|
+
case 'clickOnElementByText': {
|
|
5289
|
+
const messageId = data.params && data.params.messageId;
|
|
5290
|
+
const text = data.params && data.params.text;
|
|
5291
|
+
const target = text ? deepFindElementByExactText(document, text) : null;
|
|
5292
|
+
const success = !!target;
|
|
5293
|
+
if (target && typeof target.click === 'function') target.click();
|
|
5294
|
+
window.parent.postMessage(
|
|
5295
|
+
{
|
|
5296
|
+
source: 'qti-pci-iframe',
|
|
5297
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
5298
|
+
method: 'clickTextResponse',
|
|
5299
|
+
messageId: messageId,
|
|
5300
|
+
success: success
|
|
5301
|
+
},
|
|
5302
|
+
'*'
|
|
5303
|
+
);
|
|
5304
|
+
break;
|
|
5305
|
+
}
|
|
5650
5306
|
|
|
5651
|
-
|
|
5652
|
-
|
|
5307
|
+
case 'setValueElement': {
|
|
5308
|
+
const messageId = data.params && data.params.messageId;
|
|
5309
|
+
const selector = data.params && data.params.selector;
|
|
5310
|
+
const value = data.params && data.params.value;
|
|
5311
|
+
const el = selector ? deepQuerySelector(document, selector) : null;
|
|
5312
|
+
let success = false;
|
|
5313
|
+
if (el && 'value' in el) {
|
|
5314
|
+
try {
|
|
5315
|
+
el.value = value;
|
|
5316
|
+
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
5317
|
+
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
5318
|
+
success = true;
|
|
5319
|
+
} catch (e) {
|
|
5320
|
+
success = false;
|
|
5321
|
+
}
|
|
5322
|
+
}
|
|
5323
|
+
window.parent.postMessage(
|
|
5324
|
+
{
|
|
5325
|
+
source: 'qti-pci-iframe',
|
|
5326
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
5327
|
+
method: 'setValueResponse',
|
|
5328
|
+
messageId: messageId,
|
|
5329
|
+
success: success
|
|
5330
|
+
},
|
|
5331
|
+
'*'
|
|
5332
|
+
);
|
|
5333
|
+
break;
|
|
5334
|
+
}
|
|
5653
5335
|
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
5659
|
-
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5336
|
+
case 'console':
|
|
5337
|
+
window.parent.postMessage(
|
|
5338
|
+
{
|
|
5339
|
+
source: 'qti-pci-iframe',
|
|
5340
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
5341
|
+
method: 'console',
|
|
5342
|
+
level: data.level,
|
|
5343
|
+
args: data.args
|
|
5344
|
+
},
|
|
5345
|
+
'*'
|
|
5346
|
+
);
|
|
5347
|
+
break;
|
|
5348
|
+
}
|
|
5349
|
+
});
|
|
5664
5350
|
|
|
5665
|
-
|
|
5666
|
-
|
|
5667
|
-
|
|
5668
|
-
|
|
5669
|
-
|
|
5670
|
-
|
|
5671
|
-
|
|
5672
|
-
|
|
5673
|
-
|
|
5674
|
-
|
|
5675
|
-
|
|
5676
|
-
|
|
5677
|
-
|
|
5678
|
-
|
|
5679
|
-
|
|
5680
|
-
|
|
5681
|
-
|
|
5682
|
-
|
|
5683
|
-
|
|
5684
|
-
|
|
5685
|
-
|
|
5686
|
-
|
|
5687
|
-
|
|
5688
|
-
|
|
5689
|
-
|
|
5690
|
-
|
|
5691
|
-
|
|
5692
|
-
|
|
5693
|
-
|
|
5694
|
-
|
|
5695
|
-
|
|
5696
|
-
|
|
5697
|
-
|
|
5698
|
-
|
|
5699
|
-
|
|
5700
|
-
|
|
5701
|
-
|
|
5702
|
-
|
|
5703
|
-
|
|
5704
|
-
|
|
5705
|
-
|
|
5706
|
-
|
|
5707
|
-
|
|
5708
|
-
|
|
5709
|
-
|
|
5710
|
-
|
|
5711
|
-
|
|
5712
|
-
|
|
5713
|
-
|
|
5714
|
-
|
|
5351
|
+
let resizeTimeout;
|
|
5352
|
+
let previousHeight = 0;
|
|
5353
|
+
const notifyResize = () => {
|
|
5354
|
+
const container = document.getElementById('pci-container');
|
|
5355
|
+
const newHeight = container.scrollHeight + 100;
|
|
5356
|
+
if (newHeight !== previousHeight) {
|
|
5357
|
+
previousHeight = newHeight;
|
|
5358
|
+
clearTimeout(resizeTimeout);
|
|
5359
|
+
resizeTimeout = setTimeout(() => {
|
|
5360
|
+
window.parent.postMessage(
|
|
5361
|
+
{
|
|
5362
|
+
source: 'qti-pci-iframe',
|
|
5363
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
5364
|
+
method: 'resize',
|
|
5365
|
+
height: newHeight,
|
|
5366
|
+
width: container.scrollWidth
|
|
5367
|
+
},
|
|
5368
|
+
'*'
|
|
5369
|
+
);
|
|
5370
|
+
}, 100); // Adjust debounce time as needed
|
|
5371
|
+
}
|
|
5372
|
+
};
|
|
5373
|
+
|
|
5374
|
+
function setupResizeObserver() {
|
|
5375
|
+
const container = document.getElementById('pci-container');
|
|
5376
|
+
if (!container || !(container instanceof Element)) {
|
|
5377
|
+
console.warn('ResizeObserver: document.container is not an Element');
|
|
5378
|
+
return;
|
|
5379
|
+
}
|
|
5380
|
+
|
|
5381
|
+
const resizeObserver = new ResizeObserver(() => {
|
|
5382
|
+
notifyResize();
|
|
5383
|
+
});
|
|
5384
|
+
|
|
5385
|
+
resizeObserver.observe(container);
|
|
5386
|
+
}
|
|
5387
|
+
|
|
5388
|
+
// Run setup once DOM is ready
|
|
5389
|
+
if (document.readyState === 'loading') {
|
|
5390
|
+
document.addEventListener('DOMContentLoaded', () => {
|
|
5391
|
+
notifyResize(); // initial resize
|
|
5392
|
+
setupResizeObserver();
|
|
5393
|
+
});
|
|
5394
|
+
} else {
|
|
5395
|
+
notifyResize();
|
|
5396
|
+
setupResizeObserver();
|
|
5397
|
+
}
|
|
5398
|
+
|
|
5399
|
+
window.addEventListener('load', () => {
|
|
5400
|
+
notifyResize();
|
|
5401
|
+
});
|
|
5402
|
+
let lastResponseStr = '';
|
|
5403
|
+
setInterval(() => {
|
|
5404
|
+
if (PCIManager.interactionChangedViaEvent) return;
|
|
5405
|
+
if (PCIManager.pciInstance && PCIManager.pciInstance.getResponse) {
|
|
5406
|
+
const response = PCIManager.pciInstance.getResponse();
|
|
5407
|
+
if (response === undefined) {
|
|
5408
|
+
// Don't emit an initial empty on load; only emit a clear if we previously had a value
|
|
5409
|
+
if (!PCIManager.hadResponse) return;
|
|
5410
|
+
PCIManager.hadResponse = false;
|
|
5411
|
+
PCIManager.lastResponseStr = null;
|
|
5412
|
+
const state =
|
|
5413
|
+
PCIManager.pciInstance && typeof PCIManager.pciInstance.getState === 'function'
|
|
5414
|
+
? PCIManager.pciInstance.getState()
|
|
5415
|
+
: null;
|
|
5416
|
+
window.parent.postMessage(
|
|
5417
|
+
{
|
|
5418
|
+
source: 'qti-pci-iframe',
|
|
5419
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
5420
|
+
method: 'interactionChanged',
|
|
5421
|
+
params: { value: null, state: state }
|
|
5422
|
+
},
|
|
5423
|
+
'*'
|
|
5424
|
+
);
|
|
5425
|
+
return;
|
|
5426
|
+
}
|
|
5427
|
+
|
|
5428
|
+
const responseStr = JSON.stringify(response);
|
|
5429
|
+
|
|
5430
|
+
if (responseStr !== PCIManager.lastResponseStr) {
|
|
5431
|
+
PCIManager.lastResponseStr = responseStr;
|
|
5432
|
+
PCIManager.hadResponse = true;
|
|
5433
|
+
const state =
|
|
5434
|
+
PCIManager.pciInstance && typeof PCIManager.pciInstance.getState === 'function'
|
|
5435
|
+
? PCIManager.pciInstance.getState()
|
|
5436
|
+
: null;
|
|
5437
|
+
window.parent.postMessage(
|
|
5438
|
+
{
|
|
5439
|
+
source: 'qti-pci-iframe',
|
|
5440
|
+
responseIdentifier: PCIManager.responseIdentifier,
|
|
5441
|
+
method: 'interactionChanged',
|
|
5442
|
+
params: { value: response, state: state }
|
|
5443
|
+
},
|
|
5444
|
+
'*'
|
|
5445
|
+
);
|
|
5446
|
+
}
|
|
5447
|
+
}
|
|
5448
|
+
}, 500); // Check every 500ms
|
|
5449
|
+
</script>
|
|
5450
|
+
</head>
|
|
5451
|
+
<body>
|
|
5452
|
+
<div id="pci-container"></div>
|
|
5453
|
+
</body>
|
|
5454
|
+
</html>`
|
|
5455
|
+
);
|
|
5715
5456
|
}
|
|
5716
5457
|
/**
|
|
5717
5458
|
* Toggle the display of the correct response
|
|
@@ -7083,17 +6824,23 @@ var QtiHottext = class extends ActiveElementMixin(i2, "qti-hottext") {
|
|
|
7083
6824
|
}
|
|
7084
6825
|
};
|
|
7085
6826
|
|
|
6827
|
+
// ../qti-interactions/src/elements/qti-inline-choice/qti-inline-choice.styles.ts
|
|
6828
|
+
var styles2 = i`
|
|
6829
|
+
:host {
|
|
6830
|
+
display: block;
|
|
6831
|
+
box-sizing: border-box;
|
|
6832
|
+
}
|
|
6833
|
+
|
|
6834
|
+
slot {
|
|
6835
|
+
display: inline;
|
|
6836
|
+
}
|
|
6837
|
+
`;
|
|
6838
|
+
var qti_inline_choice_styles_default = styles2;
|
|
6839
|
+
|
|
7086
6840
|
// ../qti-interactions/src/elements/qti-inline-choice/qti-inline-choice.ts
|
|
7087
|
-
var QtiInlineChoice = class extends i2 {
|
|
7088
|
-
static
|
|
7089
|
-
|
|
7090
|
-
i`
|
|
7091
|
-
:host {
|
|
7092
|
-
display: block;
|
|
7093
|
-
cursor: pointer;
|
|
7094
|
-
}
|
|
7095
|
-
`
|
|
7096
|
-
];
|
|
6841
|
+
var QtiInlineChoice = class extends ActiveElementMixin(i2, "qti-inline-choice") {
|
|
6842
|
+
static {
|
|
6843
|
+
this.styles = qti_inline_choice_styles_default;
|
|
7097
6844
|
}
|
|
7098
6845
|
connectedCallback() {
|
|
7099
6846
|
super.connectedCallback();
|
|
@@ -7857,4 +7604,4 @@ lit-html/node/directives/ref.js:
|
|
|
7857
7604
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
7858
7605
|
*)
|
|
7859
7606
|
*/
|
|
7860
|
-
//# sourceMappingURL=chunk-
|
|
7607
|
+
//# sourceMappingURL=chunk-2X7747Q4.js.map
|