@citolab/qti-components 7.22.0 → 7.23.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 +2550 -2447
- package/custom-elements.json +91 -208
- package/dist/{chunk-J7X6NAL5.js → chunk-2EVAV2SS.js} +1117 -1365
- package/dist/chunk-2EVAV2SS.js.map +1 -0
- package/dist/{chunk-MF25NAZ4.js → chunk-5WZZMNCU.js} +2 -2
- package/dist/{chunk-MF25NAZ4.js.map → chunk-5WZZMNCU.js.map} +1 -1
- package/dist/{chunk-HCVDQUP7.js → chunk-AIS537EU.js} +1486 -1236
- package/dist/chunk-AIS537EU.js.map +1 -0
- package/dist/{chunk-YAGFD5RQ.js → chunk-DDKFUQ22.js} +2 -2
- package/dist/chunk-DDKFUQ22.js.map +1 -0
- package/dist/index.js +4 -4
- package/dist/interactions.d.ts +8 -23
- package/dist/interactions.js +1 -1
- package/dist/item.css +1484 -1234
- package/dist/item.js +2 -2
- package/dist/qti-components-jsx.d.ts +11563 -0
- package/dist/test.js +2 -2
- package/package.json +8 -8
- package/dist/chunk-HCVDQUP7.js.map +0 -1
- package/dist/chunk-J7X6NAL5.js.map +0 -1
- package/dist/chunk-YAGFD5RQ.js.map +0 -1
|
@@ -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) {
|
|
@@ -987,7 +987,7 @@ var qti_associate_interaction_styles_default = i`
|
|
|
987
987
|
align-items: flex-start;
|
|
988
988
|
flex: 1;
|
|
989
989
|
border: 2px solid transparent;
|
|
990
|
-
|
|
990
|
+
margin: 1rem 0;
|
|
991
991
|
border-radius: 0.3rem;
|
|
992
992
|
gap: 0.5rem;
|
|
993
993
|
}
|
|
@@ -997,18 +997,23 @@ var qti_associate_interaction_styles_default = i`
|
|
|
997
997
|
background-color: var(--qti-bg-active) !important;
|
|
998
998
|
}
|
|
999
999
|
|
|
1000
|
+
[part='drop-container'] {
|
|
1001
|
+
display: flex;
|
|
1002
|
+
flex-direction: column;
|
|
1003
|
+
gap: 0.5rem;
|
|
1004
|
+
}
|
|
1005
|
+
|
|
1000
1006
|
[part='drop-list'][enabled] {
|
|
1001
1007
|
background-color: var(--qti-bg-active) !important;
|
|
1002
1008
|
}
|
|
1003
1009
|
|
|
1004
1010
|
:host::part(associables-container) {
|
|
1005
1011
|
display: flex;
|
|
1006
|
-
padding: 0.5rem;
|
|
1007
1012
|
justify-content: space-between;
|
|
1008
1013
|
background: linear-gradient(
|
|
1009
1014
|
180deg,
|
|
1010
1015
|
rgb(0 0 0 / 0%) calc(50% - 1px),
|
|
1011
|
-
var(--qti-border-color
|
|
1016
|
+
var(--qti-border-color) calc(50%),
|
|
1012
1017
|
rgb(0 0 0 / 0%) calc(50% + 1px)
|
|
1013
1018
|
);
|
|
1014
1019
|
}
|
|
@@ -1219,7 +1224,7 @@ var ChoicesMixin = (superClass, selector) => {
|
|
|
1219
1224
|
this._setInputType(choice);
|
|
1220
1225
|
});
|
|
1221
1226
|
}
|
|
1222
|
-
_setInputType(choiceElement) {
|
|
1227
|
+
async _setInputType(choiceElement) {
|
|
1223
1228
|
this._internals.role = this.maxChoices === 1 ? "radiogroup" : null;
|
|
1224
1229
|
if (choiceElement.internals) {
|
|
1225
1230
|
const role = this.maxChoices === 1 ? "radio" : "checkbox";
|
|
@@ -1302,7 +1307,7 @@ var ChoicesMixin = (superClass, selector) => {
|
|
|
1302
1307
|
n({ type: Number, attribute: "max-choices" })
|
|
1303
1308
|
], ChoicesMixinElement.prototype, "maxChoices", 2);
|
|
1304
1309
|
__decorateClass([
|
|
1305
|
-
watch("maxChoices"
|
|
1310
|
+
watch("maxChoices")
|
|
1306
1311
|
], ChoicesMixinElement.prototype, "_handleMaxChoicesChange", 1);
|
|
1307
1312
|
__decorateClass([
|
|
1308
1313
|
watch("disabled", { waitUntilFirstUpdate: true })
|
|
@@ -2987,24 +2992,80 @@ var e3 = class extends i3 {
|
|
|
2987
2992
|
e3.directiveName = "unsafeHTML", e3.resultType = 1;
|
|
2988
2993
|
var o3 = e2(e3);
|
|
2989
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
|
+
|
|
2990
3055
|
// ../qti-interactions/src/components/qti-inline-choice-interaction/qti-inline-choice-interaction.ts
|
|
2991
|
-
var
|
|
3056
|
+
var inlineChoiceMenuCounter = 0;
|
|
3057
|
+
var QtiInlineChoiceInteraction = class extends Interaction {
|
|
2992
3058
|
constructor() {
|
|
2993
|
-
super(
|
|
3059
|
+
super();
|
|
2994
3060
|
this.options = [];
|
|
2995
3061
|
this.correctOption = "";
|
|
2996
3062
|
this._dropdownOpen = false;
|
|
2997
|
-
this.
|
|
2998
|
-
this.
|
|
2999
|
-
this
|
|
3000
|
-
|
|
3001
|
-
|
|
3002
|
-
|
|
3003
|
-
this.#selectValue(selectedOptionValue);
|
|
3004
|
-
};
|
|
3005
|
-
this.#onToggleCustomDropdown = () => {
|
|
3006
|
-
if (this.disabled || this.readonly) return;
|
|
3007
|
-
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
|
+
}
|
|
3008
3069
|
};
|
|
3009
3070
|
this.#onCustomTriggerKeyDown = (event) => {
|
|
3010
3071
|
if (this.disabled || this.readonly) return;
|
|
@@ -3013,6 +3074,13 @@ var _QtiInlineChoiceInteraction = class _QtiInlineChoiceInteraction extends Inte
|
|
|
3013
3074
|
this.#setDropdownOpen(true);
|
|
3014
3075
|
}
|
|
3015
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
|
+
};
|
|
3016
3084
|
this.#onCustomMenuKeyDown = (event) => {
|
|
3017
3085
|
if (!this._dropdownOpen) return;
|
|
3018
3086
|
if (event.key === "Escape") {
|
|
@@ -3021,417 +3089,125 @@ var _QtiInlineChoiceInteraction = class _QtiInlineChoiceInteraction extends Inte
|
|
|
3021
3089
|
this.#focusTrigger();
|
|
3022
3090
|
return;
|
|
3023
3091
|
}
|
|
3024
|
-
const
|
|
3025
|
-
const
|
|
3026
|
-
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);
|
|
3027
3097
|
if (event.key === "ArrowDown") {
|
|
3028
3098
|
event.preventDefault();
|
|
3029
|
-
|
|
3099
|
+
optionElements[Math.min(optionElements.length - 1, Math.max(0, activeIndex + 1))]?.focus();
|
|
3030
3100
|
}
|
|
3031
3101
|
if (event.key === "ArrowUp") {
|
|
3032
3102
|
event.preventDefault();
|
|
3033
|
-
|
|
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
|
+
}
|
|
3034
3114
|
}
|
|
3035
3115
|
};
|
|
3036
|
-
this.#
|
|
3037
|
-
|
|
3038
|
-
const path = event.composedPath?.();
|
|
3039
|
-
if (path && path.includes(this)) return;
|
|
3040
|
-
this.#setDropdownOpen(false);
|
|
3116
|
+
this.#onChoicesSlotChange = () => {
|
|
3117
|
+
this.#updateOptions();
|
|
3041
3118
|
};
|
|
3042
|
-
this.#
|
|
3043
|
-
if (
|
|
3044
|
-
|
|
3045
|
-
|
|
3046
|
-
this.#
|
|
3047
|
-
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);
|
|
3048
3124
|
};
|
|
3125
|
+
this.internals.role = "listbox";
|
|
3049
3126
|
}
|
|
3050
3127
|
get isInline() {
|
|
3051
3128
|
return true;
|
|
3052
3129
|
}
|
|
3053
|
-
static {
|
|
3054
|
-
this._supportsCustomizableSelectCache = null;
|
|
3055
|
-
}
|
|
3056
3130
|
static get styles() {
|
|
3057
|
-
return [
|
|
3058
|
-
i`
|
|
3059
|
-
:host {
|
|
3060
|
-
display: inline-block;
|
|
3061
|
-
vertical-align: baseline;
|
|
3062
|
-
position: relative;
|
|
3063
|
-
}
|
|
3064
|
-
|
|
3065
|
-
/* --- Progressive enhancement: Customizable select (MDN / WHATWG) --- */
|
|
3066
|
-
select[part='select'] {
|
|
3067
|
-
font: inherit;
|
|
3068
|
-
color: inherit;
|
|
3069
|
-
background-color: var(--qti-bg, white);
|
|
3070
|
-
border: var(--qti-border-thickness, 2px) var(--qti-border-style, solid) var(--qti-border-color, #c6cad0);
|
|
3071
|
-
border-radius: var(--qti-border-radius, 0.3rem);
|
|
3072
|
-
padding: 0.25rem 0.75rem;
|
|
3073
|
-
min-width: var(--qti-calculated-min-width, auto);
|
|
3074
|
-
/* Enables full styling when supported (Chromium behind a flag / rolling out). */
|
|
3075
|
-
appearance: base-select;
|
|
3076
|
-
}
|
|
3077
|
-
|
|
3078
|
-
select[part='select']:disabled {
|
|
3079
|
-
opacity: 0.6;
|
|
3080
|
-
cursor: not-allowed;
|
|
3081
|
-
}
|
|
3082
|
-
|
|
3083
|
-
select[part='select']::picker(select) {
|
|
3084
|
-
border: var(--qti-border-thickness, 2px) var(--qti-border-style, solid) var(--qti-border-color, #c6cad0);
|
|
3085
|
-
border-radius: var(--qti-border-radius, 0.3rem);
|
|
3086
|
-
background: var(--qti-bg, white);
|
|
3087
|
-
box-shadow:
|
|
3088
|
-
0 10px 15px -3px rgb(0 0 0 / 10%),
|
|
3089
|
-
0 4px 6px -4px rgb(0 0 0 / 10%);
|
|
3090
|
-
padding: 4px;
|
|
3091
|
-
width: max-content;
|
|
3092
|
-
min-width: 100%;
|
|
3093
|
-
max-width: min(90vw, 36rem);
|
|
3094
|
-
}
|
|
3095
|
-
|
|
3096
|
-
select[part='select']::picker-icon {
|
|
3097
|
-
color: var(--qti-border-color, #c6cad0);
|
|
3098
|
-
transition: 0.4s rotate;
|
|
3099
|
-
font-size: 1.75em;
|
|
3100
|
-
}
|
|
3101
|
-
|
|
3102
|
-
select[part='select']:open::picker-icon {
|
|
3103
|
-
color: var(--qti-border-active, #f86d70);
|
|
3104
|
-
rotate: 180deg;
|
|
3105
|
-
}
|
|
3106
|
-
|
|
3107
|
-
select[part='select'] > button {
|
|
3108
|
-
font: inherit;
|
|
3109
|
-
color: inherit;
|
|
3110
|
-
display: inline-flex;
|
|
3111
|
-
align-items: center;
|
|
3112
|
-
gap: 0.25rem;
|
|
3113
|
-
padding: 0;
|
|
3114
|
-
background: transparent;
|
|
3115
|
-
border: 0;
|
|
3116
|
-
cursor: pointer;
|
|
3117
|
-
}
|
|
3118
|
-
|
|
3119
|
-
select[part='select'] selectedcontent {
|
|
3120
|
-
display: inline-flex;
|
|
3121
|
-
align-items: center;
|
|
3122
|
-
gap: 0.5rem;
|
|
3123
|
-
white-space: nowrap;
|
|
3124
|
-
}
|
|
3125
|
-
|
|
3126
|
-
option {
|
|
3127
|
-
font: inherit;
|
|
3128
|
-
color: inherit;
|
|
3129
|
-
display: flex;
|
|
3130
|
-
align-items: center;
|
|
3131
|
-
gap: 0.5rem;
|
|
3132
|
-
padding: 0.5rem 0.5rem;
|
|
3133
|
-
white-space: nowrap;
|
|
3134
|
-
line-height: 1.25;
|
|
3135
|
-
min-height: 2.25rem;
|
|
3136
|
-
}
|
|
3137
|
-
|
|
3138
|
-
option:hover {
|
|
3139
|
-
background-color: var(--qti-hover-bg, #f9fafb);
|
|
3140
|
-
}
|
|
3141
|
-
|
|
3142
|
-
option:checked {
|
|
3143
|
-
background-color: var(--qti-bg-active, #ffecec);
|
|
3144
|
-
}
|
|
3145
|
-
|
|
3146
|
-
option::checkmark {
|
|
3147
|
-
color: var(--qti-border-active, #f86d70);
|
|
3148
|
-
}
|
|
3149
|
-
|
|
3150
|
-
/* --- Fallback custom listbox (for browsers without customizable select) --- */
|
|
3151
|
-
button[part='trigger'] {
|
|
3152
|
-
font: inherit;
|
|
3153
|
-
color: inherit;
|
|
3154
|
-
background-color: var(--qti-bg, white);
|
|
3155
|
-
cursor: pointer;
|
|
3156
|
-
display: inline-flex;
|
|
3157
|
-
align-items: center;
|
|
3158
|
-
gap: 0.5rem;
|
|
3159
|
-
justify-content: space-between;
|
|
3160
|
-
border: var(--qti-border-thickness, 2px) var(--qti-border-style, solid) var(--qti-border-color, #c6cad0);
|
|
3161
|
-
border-radius: var(--qti-border-radius, 0.3rem);
|
|
3162
|
-
padding: 0.25rem 0.75rem;
|
|
3163
|
-
min-width: var(--qti-calculated-min-width, auto);
|
|
3164
|
-
}
|
|
3165
|
-
|
|
3166
|
-
[part='value'] {
|
|
3167
|
-
display: inline-flex;
|
|
3168
|
-
align-items: center;
|
|
3169
|
-
gap: 0.5rem;
|
|
3170
|
-
min-width: 0;
|
|
3171
|
-
}
|
|
3172
|
-
|
|
3173
|
-
[part='dropdown-icon'] {
|
|
3174
|
-
display: inline-flex;
|
|
3175
|
-
align-items: center;
|
|
3176
|
-
justify-content: center;
|
|
3177
|
-
flex: 0 0 auto;
|
|
3178
|
-
transition: transform 150ms ease;
|
|
3179
|
-
transform-origin: 50% 50%;
|
|
3180
|
-
color: var(--qti-border-color, #c6cad0);
|
|
3181
|
-
font-size: 1.75em;
|
|
3182
|
-
line-height: 1;
|
|
3183
|
-
}
|
|
3184
|
-
|
|
3185
|
-
button[part='trigger'][aria-expanded='true'] [part='dropdown-icon'] {
|
|
3186
|
-
transform: rotate(180deg);
|
|
3187
|
-
color: var(--qti-border-active, #f86d70);
|
|
3188
|
-
}
|
|
3189
|
-
|
|
3190
|
-
button[part='trigger'][disabled] {
|
|
3191
|
-
cursor: not-allowed;
|
|
3192
|
-
opacity: 0.6;
|
|
3193
|
-
}
|
|
3194
|
-
|
|
3195
|
-
[part='menu'] {
|
|
3196
|
-
position: absolute;
|
|
3197
|
-
z-index: 1000;
|
|
3198
|
-
top: calc(100% + 4px);
|
|
3199
|
-
left: 0;
|
|
3200
|
-
min-width: 100%;
|
|
3201
|
-
max-width: min(90vw, 36rem);
|
|
3202
|
-
max-height: min(40vh, 20rem);
|
|
3203
|
-
overflow: auto;
|
|
3204
|
-
background-color: var(--qti-bg, white);
|
|
3205
|
-
border: var(--qti-border-thickness, 2px) var(--qti-border-style, solid) var(--qti-border-color, #c6cad0);
|
|
3206
|
-
border-radius: var(--qti-border-radius, 0.3rem);
|
|
3207
|
-
box-shadow:
|
|
3208
|
-
0 10px 15px -3px rgb(0 0 0 / 10%),
|
|
3209
|
-
0 4px 6px -4px rgb(0 0 0 / 10%);
|
|
3210
|
-
padding: 4px;
|
|
3211
|
-
box-sizing: border-box;
|
|
3212
|
-
transform: translate(var(--qti-menu-shift-x, 0px), var(--qti-menu-shift-y, 0px));
|
|
3213
|
-
}
|
|
3214
|
-
|
|
3215
|
-
[part='menu'][data-placement='top'] {
|
|
3216
|
-
top: auto;
|
|
3217
|
-
bottom: calc(100% + 4px);
|
|
3218
|
-
}
|
|
3219
|
-
|
|
3220
|
-
button[part='option'] {
|
|
3221
|
-
font: inherit;
|
|
3222
|
-
color: inherit;
|
|
3223
|
-
background-color: transparent;
|
|
3224
|
-
border: 0;
|
|
3225
|
-
padding: 0.5rem 0.5rem;
|
|
3226
|
-
width: 100%;
|
|
3227
|
-
text-align: left;
|
|
3228
|
-
border-radius: calc(var(--qti-border-radius, 0.3rem) - 2px);
|
|
3229
|
-
cursor: pointer;
|
|
3230
|
-
white-space: nowrap;
|
|
3231
|
-
overflow: hidden;
|
|
3232
|
-
text-overflow: ellipsis;
|
|
3233
|
-
line-height: 1.25;
|
|
3234
|
-
min-height: 2.25rem;
|
|
3235
|
-
}
|
|
3236
|
-
|
|
3237
|
-
button[part='option'][aria-selected='true'] {
|
|
3238
|
-
background-color: var(--qti-bg-active, #ffecec);
|
|
3239
|
-
}
|
|
3240
|
-
|
|
3241
|
-
button[part='option']:hover {
|
|
3242
|
-
background-color: var(--qti-hover-bg, #f9fafb);
|
|
3243
|
-
}
|
|
3244
|
-
|
|
3245
|
-
button[part='option']:focus-visible {
|
|
3246
|
-
outline: 2px solid var(--qti-border-active, #f86d70);
|
|
3247
|
-
outline-offset: 2px;
|
|
3248
|
-
}
|
|
3249
|
-
|
|
3250
|
-
[part='option-content'] {
|
|
3251
|
-
display: flex;
|
|
3252
|
-
align-items: center;
|
|
3253
|
-
gap: 0.5rem;
|
|
3254
|
-
flex-wrap: nowrap;
|
|
3255
|
-
white-space: nowrap;
|
|
3256
|
-
overflow: hidden;
|
|
3257
|
-
text-overflow: ellipsis;
|
|
3258
|
-
min-width: 0;
|
|
3259
|
-
}
|
|
3260
|
-
|
|
3261
|
-
select[part='select'] img,
|
|
3262
|
-
button[part='option'] img,
|
|
3263
|
-
button[part='trigger'] img,
|
|
3264
|
-
[part='menu'] img {
|
|
3265
|
-
display: inline-block;
|
|
3266
|
-
max-height: 1em;
|
|
3267
|
-
max-width: 1.5em;
|
|
3268
|
-
vertical-align: middle;
|
|
3269
|
-
}
|
|
3270
|
-
`
|
|
3271
|
-
];
|
|
3272
|
-
}
|
|
3273
|
-
static {
|
|
3274
|
-
this.inputWidthClass = [
|
|
3275
|
-
"",
|
|
3276
|
-
"qti-input-width-2",
|
|
3277
|
-
"qti-input-width-1",
|
|
3278
|
-
"qti-input-width-3",
|
|
3279
|
-
"qti-input-width-4",
|
|
3280
|
-
"qti-input-width-6",
|
|
3281
|
-
"qti-input-width-10",
|
|
3282
|
-
"qti-input-width-15",
|
|
3283
|
-
"qti-input-width-20",
|
|
3284
|
-
"qti-input-width-72"
|
|
3285
|
-
];
|
|
3131
|
+
return [qti_inline_choice_interaction_styles_default];
|
|
3286
3132
|
}
|
|
3287
3133
|
render() {
|
|
3288
3134
|
const selected = this.#selectedOption();
|
|
3289
|
-
const useCustomizableSelect = this.#supportsCustomizableSelect();
|
|
3290
3135
|
return x`
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
|
|
3295
|
-
|
|
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
|
-
@click="${() => this.#selectValue(option.value)}"
|
|
3329
|
-
>
|
|
3330
|
-
<span part="option-content">${o3(option.textContent)}</span>
|
|
3331
|
-
</button>
|
|
3332
|
-
`
|
|
3333
|
-
)}
|
|
3334
|
-
</div>
|
|
3335
|
-
` : null}
|
|
3336
|
-
`}
|
|
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>
|
|
3337
3173
|
${o3(this.correctOption)}
|
|
3338
3174
|
`;
|
|
3339
3175
|
}
|
|
3340
|
-
connectedCallback() {
|
|
3176
|
+
async connectedCallback() {
|
|
3341
3177
|
super.connectedCallback();
|
|
3342
3178
|
this.#updateOptions();
|
|
3343
|
-
|
|
3344
|
-
|
|
3345
|
-
|
|
3346
|
-
}
|
|
3347
|
-
this._estimateOptimalWidth();
|
|
3179
|
+
this.#startSlotObserver();
|
|
3180
|
+
await this.updateComplete;
|
|
3181
|
+
this.#estimateOptimalWidth();
|
|
3348
3182
|
}
|
|
3349
3183
|
disconnectedCallback() {
|
|
3350
3184
|
super.disconnectedCallback();
|
|
3351
|
-
|
|
3352
|
-
|
|
3353
|
-
|
|
3354
|
-
}
|
|
3355
|
-
if (this._widthCalculationTimer !== null) {
|
|
3356
|
-
window.clearTimeout(this._widthCalculationTimer);
|
|
3357
|
-
this._widthCalculationTimer = null;
|
|
3358
|
-
}
|
|
3185
|
+
this.#teardownSlottedChoices();
|
|
3186
|
+
this._slotObserver?.disconnect();
|
|
3187
|
+
this._slotObserver = null;
|
|
3359
3188
|
}
|
|
3360
3189
|
willUpdate(changed) {
|
|
3361
|
-
if (changed.has("configContext")
|
|
3190
|
+
if (changed.has("configContext")) {
|
|
3362
3191
|
this.#updateOptions();
|
|
3363
3192
|
}
|
|
3364
3193
|
}
|
|
3365
3194
|
updated(changed) {
|
|
3366
3195
|
const dropdownOpenKey = "_dropdownOpen";
|
|
3367
3196
|
if (changed.has(dropdownOpenKey) && this._dropdownOpen) {
|
|
3368
|
-
this.#
|
|
3369
|
-
const
|
|
3370
|
-
|
|
3197
|
+
this.#syncSlottedChoices();
|
|
3198
|
+
const first = this.#allMenuOptions()[0];
|
|
3199
|
+
first?.focus();
|
|
3200
|
+
}
|
|
3201
|
+
if (changed.has("disabled") || changed.has("readonly")) {
|
|
3202
|
+
this.#syncSlottedChoices();
|
|
3371
3203
|
}
|
|
3372
3204
|
}
|
|
3373
3205
|
#selectedOption() {
|
|
3374
3206
|
return this.options.find((option) => option.selected) ?? this.options[0];
|
|
3375
3207
|
}
|
|
3376
|
-
/**
|
|
3377
|
-
* Progressive enhancement for "customizable select" (WHATWG / MDN: `appearance: base-select` + `::picker()`).
|
|
3378
|
-
*
|
|
3379
|
-
* Notes on current browser behavior (observed around Feb 2026):
|
|
3380
|
-
* - Chromium-based browsers can support customizable select in light DOM, but it does not reliably work when the
|
|
3381
|
-
* `<select>` lives inside a shadow root (e.g. the internal `<button>/<selectedcontent>` can end up effectively
|
|
3382
|
-
* not rendered, so rich content like images disappears).
|
|
3383
|
-
* - Firefox support is not generally available yet, so we fall back to our custom listbox there as well.
|
|
3384
|
-
*
|
|
3385
|
-
* Because `CSS.supports(...)` may return syntax-only true, we do a final DOM probe to ensure the customizable-select
|
|
3386
|
-
* markup actually takes effect in the current environment before opting in.
|
|
3387
|
-
*/
|
|
3388
|
-
#supportsCustomizableSelect() {
|
|
3389
|
-
if (_QtiInlineChoiceInteraction._supportsCustomizableSelectCache !== null) {
|
|
3390
|
-
return _QtiInlineChoiceInteraction._supportsCustomizableSelectCache;
|
|
3391
|
-
}
|
|
3392
|
-
if (typeof CSS === "undefined" || typeof CSS.supports !== "function") {
|
|
3393
|
-
_QtiInlineChoiceInteraction._supportsCustomizableSelectCache = false;
|
|
3394
|
-
return false;
|
|
3395
|
-
}
|
|
3396
|
-
const supportsPickerSelector = CSS.supports("selector(::picker(select))") || CSS.supports("selector(select::picker(select))");
|
|
3397
|
-
const supportsAppearanceValue = CSS.supports("appearance: base-select") || CSS.supports("-webkit-appearance: base-select");
|
|
3398
|
-
if (!supportsPickerSelector || !supportsAppearanceValue) {
|
|
3399
|
-
_QtiInlineChoiceInteraction._supportsCustomizableSelectCache = false;
|
|
3400
|
-
return false;
|
|
3401
|
-
}
|
|
3402
|
-
try {
|
|
3403
|
-
const container = document.createElement("div");
|
|
3404
|
-
container.style.position = "absolute";
|
|
3405
|
-
container.style.top = "-9999px";
|
|
3406
|
-
container.style.left = "-9999px";
|
|
3407
|
-
const select = document.createElement("select");
|
|
3408
|
-
select.style.appearance = "base-select";
|
|
3409
|
-
select.style.webkitAppearance = "base-select";
|
|
3410
|
-
const button = document.createElement("button");
|
|
3411
|
-
button.type = "button";
|
|
3412
|
-
const selected = document.createElement("selectedcontent");
|
|
3413
|
-
selected.textContent = "probe";
|
|
3414
|
-
button.appendChild(selected);
|
|
3415
|
-
const option = document.createElement("option");
|
|
3416
|
-
option.value = "probe";
|
|
3417
|
-
option.textContent = "probe";
|
|
3418
|
-
select.appendChild(button);
|
|
3419
|
-
select.appendChild(option);
|
|
3420
|
-
container.appendChild(select);
|
|
3421
|
-
(document.body || document.documentElement).appendChild(container);
|
|
3422
|
-
const rect = button.getBoundingClientRect();
|
|
3423
|
-
container.remove();
|
|
3424
|
-
const supported = rect.width > 0 && rect.height > 0;
|
|
3425
|
-
_QtiInlineChoiceInteraction._supportsCustomizableSelectCache = supported;
|
|
3426
|
-
return supported;
|
|
3427
|
-
} catch {
|
|
3428
|
-
_QtiInlineChoiceInteraction._supportsCustomizableSelectCache = false;
|
|
3429
|
-
return false;
|
|
3430
|
-
}
|
|
3431
|
-
}
|
|
3432
3208
|
#updateOptions() {
|
|
3433
3209
|
const choices = Array.from(this.querySelectorAll("qti-inline-choice"));
|
|
3434
|
-
const prompt = this.
|
|
3210
|
+
const prompt = this.dataset.prompt || this.configContext?.inlineChoicePrompt || "select";
|
|
3435
3211
|
const currentlySelectedValue = this.options.find((o6) => o6.selected)?.value ?? "";
|
|
3436
3212
|
const nextOptions = [
|
|
3437
3213
|
{
|
|
@@ -3450,22 +3226,31 @@ var _QtiInlineChoiceInteraction = class _QtiInlineChoiceInteraction extends Inte
|
|
|
3450
3226
|
];
|
|
3451
3227
|
const hasSelected = nextOptions.some((o6) => o6.selected);
|
|
3452
3228
|
this.options = hasSelected ? nextOptions : nextOptions.map((o6, i5) => ({ ...o6, selected: i5 === 0 }));
|
|
3453
|
-
this
|
|
3229
|
+
this.#syncSlottedChoices();
|
|
3230
|
+
this.#estimateOptimalWidth();
|
|
3454
3231
|
}
|
|
3455
|
-
|
|
3456
|
-
|
|
3457
|
-
|
|
3458
|
-
|
|
3459
|
-
|
|
3460
|
-
|
|
3461
|
-
|
|
3462
|
-
|
|
3463
|
-
|
|
3464
|
-
|
|
3465
|
-
|
|
3466
|
-
|
|
3467
|
-
|
|
3468
|
-
|
|
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`;
|
|
3469
3254
|
}
|
|
3470
3255
|
validate() {
|
|
3471
3256
|
const selectedOption = this.options.find((option) => option.selected);
|
|
@@ -3474,10 +3259,12 @@ var _QtiInlineChoiceInteraction = class _QtiInlineChoiceInteraction extends Inte
|
|
|
3474
3259
|
reset() {
|
|
3475
3260
|
this.#setDropdownOpen(false);
|
|
3476
3261
|
this.options = this.options.map((option, i5) => ({ ...option, selected: i5 === 0 }));
|
|
3262
|
+
this.#syncSlottedChoices();
|
|
3477
3263
|
}
|
|
3478
3264
|
set response(value) {
|
|
3479
3265
|
const nextValue = value ?? "";
|
|
3480
3266
|
this.options = this.options.map((option) => ({ ...option, selected: option.value === nextValue }));
|
|
3267
|
+
this.#syncSlottedChoices();
|
|
3481
3268
|
}
|
|
3482
3269
|
get response() {
|
|
3483
3270
|
const value = this.options.find((option) => option.selected)?.value ?? "";
|
|
@@ -3497,74 +3284,107 @@ var _QtiInlineChoiceInteraction = class _QtiInlineChoiceInteraction extends Inte
|
|
|
3497
3284
|
}
|
|
3498
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>`;
|
|
3499
3286
|
}
|
|
3500
|
-
#onNativeChange;
|
|
3501
3287
|
#selectValue(value) {
|
|
3502
3288
|
this.options = this.options.map((option) => ({ ...option, selected: option.value === value }));
|
|
3289
|
+
this.#syncSlottedChoices();
|
|
3503
3290
|
this.saveResponse(value);
|
|
3504
3291
|
this.#setDropdownOpen(false);
|
|
3505
3292
|
}
|
|
3506
3293
|
#setDropdownOpen(open) {
|
|
3507
|
-
|
|
3508
|
-
|
|
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
|
+
}
|
|
3509
3305
|
}
|
|
3510
|
-
#
|
|
3306
|
+
#onTriggerClick;
|
|
3511
3307
|
#onCustomTriggerKeyDown;
|
|
3308
|
+
#onMenuToggle;
|
|
3512
3309
|
#onCustomMenuKeyDown;
|
|
3513
3310
|
#focusTrigger() {
|
|
3514
3311
|
this.renderRoot.querySelector('button[part="trigger"]')?.focus();
|
|
3515
3312
|
}
|
|
3516
|
-
#
|
|
3517
|
-
|
|
3518
|
-
|
|
3519
|
-
|
|
3520
|
-
|
|
3521
|
-
|
|
3522
|
-
|
|
3523
|
-
|
|
3524
|
-
|
|
3525
|
-
|
|
3526
|
-
|
|
3527
|
-
|
|
3528
|
-
|
|
3529
|
-
|
|
3530
|
-
|
|
3531
|
-
|
|
3532
|
-
|
|
3533
|
-
|
|
3534
|
-
|
|
3535
|
-
|
|
3536
|
-
const
|
|
3537
|
-
const
|
|
3538
|
-
|
|
3539
|
-
|
|
3540
|
-
|
|
3541
|
-
|
|
3542
|
-
|
|
3543
|
-
|
|
3544
|
-
|
|
3545
|
-
|
|
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];
|
|
3546
3373
|
}
|
|
3547
3374
|
};
|
|
3548
3375
|
__decorateClass([
|
|
3549
3376
|
r()
|
|
3550
|
-
],
|
|
3551
|
-
__decorateClass([
|
|
3552
|
-
r()
|
|
3553
|
-
], _QtiInlineChoiceInteraction.prototype, "correctOption", 2);
|
|
3377
|
+
], QtiInlineChoiceInteraction.prototype, "options", 2);
|
|
3554
3378
|
__decorateClass([
|
|
3555
3379
|
r()
|
|
3556
|
-
],
|
|
3380
|
+
], QtiInlineChoiceInteraction.prototype, "correctOption", 2);
|
|
3557
3381
|
__decorateClass([
|
|
3558
3382
|
r()
|
|
3559
|
-
],
|
|
3560
|
-
__decorateClass([
|
|
3561
|
-
n({ attribute: "data-prompt", type: String })
|
|
3562
|
-
], _QtiInlineChoiceInteraction.prototype, "dataPrompt", 2);
|
|
3383
|
+
], QtiInlineChoiceInteraction.prototype, "_dropdownOpen", 2);
|
|
3563
3384
|
__decorateClass([
|
|
3564
3385
|
c({ context: configContext, subscribe: true }),
|
|
3565
3386
|
n({ attribute: false })
|
|
3566
|
-
],
|
|
3567
|
-
var QtiInlineChoiceInteraction = _QtiInlineChoiceInteraction;
|
|
3387
|
+
], QtiInlineChoiceInteraction.prototype, "configContext", 2);
|
|
3568
3388
|
|
|
3569
3389
|
// ../qti-interactions/src/components/qti-match-interaction/qti-match-interaction.styles.ts
|
|
3570
3390
|
var qti_match_interaction_styles_default = i`
|
|
@@ -4604,94 +4424,6 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4604
4424
|
}
|
|
4605
4425
|
return unescaped;
|
|
4606
4426
|
}
|
|
4607
|
-
/**
|
|
4608
|
-
* Resolve stylesheet href against baseUrl or document origin
|
|
4609
|
-
*/
|
|
4610
|
-
resolveStylesheetHref(href) {
|
|
4611
|
-
if (!href) return href;
|
|
4612
|
-
if (href.startsWith("http://") || href.startsWith("https://")) {
|
|
4613
|
-
return href;
|
|
4614
|
-
}
|
|
4615
|
-
if (href.startsWith("//")) {
|
|
4616
|
-
return `${window.location.protocol}${href}`;
|
|
4617
|
-
}
|
|
4618
|
-
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;
|
|
4619
|
-
const normalizedBase = base.endsWith("/") ? base : `${base}/`;
|
|
4620
|
-
try {
|
|
4621
|
-
return new URL(href, normalizedBase).toString();
|
|
4622
|
-
} catch {
|
|
4623
|
-
return href;
|
|
4624
|
-
}
|
|
4625
|
-
}
|
|
4626
|
-
/**
|
|
4627
|
-
* Collect qti-stylesheet elements for iframe injection
|
|
4628
|
-
*/
|
|
4629
|
-
getStylesheetConfigs() {
|
|
4630
|
-
const stylesheets = this.getDirectChildrenByTag("qti-stylesheet");
|
|
4631
|
-
if (!stylesheets.length) return [];
|
|
4632
|
-
return stylesheets.map((el, index) => {
|
|
4633
|
-
const href = el.getAttribute("href");
|
|
4634
|
-
if (href) {
|
|
4635
|
-
const resolved = this.resolveStylesheetHref(href);
|
|
4636
|
-
return { href: resolved, scoped: false, key: resolved };
|
|
4637
|
-
}
|
|
4638
|
-
const content = el.textContent?.trim();
|
|
4639
|
-
if (content) {
|
|
4640
|
-
return { content, scoped: false, key: `inline-${index}` };
|
|
4641
|
-
}
|
|
4642
|
-
return null;
|
|
4643
|
-
}).filter(Boolean);
|
|
4644
|
-
}
|
|
4645
|
-
getSharedStylesheetContent() {
|
|
4646
|
-
let cssText = "";
|
|
4647
|
-
const seen = /* @__PURE__ */ new Set();
|
|
4648
|
-
const sheets = Array.from(document.styleSheets || []);
|
|
4649
|
-
for (const sheet of sheets) {
|
|
4650
|
-
try {
|
|
4651
|
-
if (sheet.href && !sheet.href.startsWith(window.location.origin)) {
|
|
4652
|
-
continue;
|
|
4653
|
-
}
|
|
4654
|
-
const ownerNode = sheet.ownerNode;
|
|
4655
|
-
if (ownerNode && ownerNode.tagName === "STYLE") {
|
|
4656
|
-
const text = ownerNode.textContent || "";
|
|
4657
|
-
if (text && !seen.has(text)) {
|
|
4658
|
-
cssText += `${text}
|
|
4659
|
-
`;
|
|
4660
|
-
seen.add(text);
|
|
4661
|
-
}
|
|
4662
|
-
continue;
|
|
4663
|
-
}
|
|
4664
|
-
const rules = sheet.cssRules ? Array.from(sheet.cssRules) : [];
|
|
4665
|
-
if (rules.length) {
|
|
4666
|
-
const text = rules.map((rule) => rule.cssText).join("\n");
|
|
4667
|
-
if (text && !seen.has(text)) {
|
|
4668
|
-
cssText += `${text}
|
|
4669
|
-
`;
|
|
4670
|
-
seen.add(text);
|
|
4671
|
-
}
|
|
4672
|
-
}
|
|
4673
|
-
} catch {
|
|
4674
|
-
}
|
|
4675
|
-
}
|
|
4676
|
-
const trimmed = cssText.trim();
|
|
4677
|
-
return trimmed.length ? trimmed : null;
|
|
4678
|
-
}
|
|
4679
|
-
getSharedStylesheetConfig() {
|
|
4680
|
-
const content = this.getSharedStylesheetContent();
|
|
4681
|
-
if (!content) return null;
|
|
4682
|
-
return { content, scoped: false, key: "__qti_shared_css__" };
|
|
4683
|
-
}
|
|
4684
|
-
/**
|
|
4685
|
-
* IFRAME MODE: Add stylesheets to iframe
|
|
4686
|
-
*/
|
|
4687
|
-
#addStylesheetsToIframe() {
|
|
4688
|
-
const stylesheets = this.getStylesheetConfigs();
|
|
4689
|
-
const shared = this.getSharedStylesheetConfig();
|
|
4690
|
-
const payload = shared ? [shared, ...stylesheets] : stylesheets;
|
|
4691
|
-
if (payload.length > 0) {
|
|
4692
|
-
this.sendMessageToIframe("setStylesheets", payload);
|
|
4693
|
-
}
|
|
4694
|
-
}
|
|
4695
4427
|
disconnectedCallback() {
|
|
4696
4428
|
super.disconnectedCallback();
|
|
4697
4429
|
window.removeEventListener("message", this.handleIframeMessage);
|
|
@@ -4754,7 +4486,6 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4754
4486
|
this._iframeObjectUrl = null;
|
|
4755
4487
|
}
|
|
4756
4488
|
this.#addMarkupToIframe();
|
|
4757
|
-
this.#addStylesheetsToIframe();
|
|
4758
4489
|
this.#sendIframeInitData();
|
|
4759
4490
|
};
|
|
4760
4491
|
const iframeName = `qti-pci-${this.responseIdentifier}-${Date.now()}`;
|
|
@@ -4811,13 +4542,18 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4811
4542
|
return modules;
|
|
4812
4543
|
}
|
|
4813
4544
|
/**
|
|
4814
|
-
* IFRAME MODE: Add markup and properties to iframe
|
|
4545
|
+
* IFRAME MODE: Add markup, stylesheets, and properties to iframe
|
|
4815
4546
|
*/
|
|
4816
4547
|
#addMarkupToIframe() {
|
|
4817
4548
|
const markup = this.querySelector("qti-interaction-markup");
|
|
4818
4549
|
if (markup) {
|
|
4819
4550
|
this.sendMessageToIframe("setMarkup", markup.innerHTML);
|
|
4820
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
|
+
}
|
|
4821
4557
|
const properties = this.querySelector("properties");
|
|
4822
4558
|
if (properties) {
|
|
4823
4559
|
this.sendMessageToIframe("setProperties", properties.innerHTML);
|
|
@@ -4850,863 +4586,873 @@ var QtiPortableCustomInteraction = class extends Interaction {
|
|
|
4850
4586
|
font-weight: ${parentStyles.getPropertyValue("font-weight")};
|
|
4851
4587
|
color: ${parentStyles.getPropertyValue("color")};
|
|
4852
4588
|
`;
|
|
4853
|
-
return
|
|
4854
|
-
|
|
4855
|
-
|
|
4856
|
-
|
|
4857
|
-
|
|
4858
|
-
|
|
4859
|
-
|
|
4860
|
-
|
|
4861
|
-
|
|
4862
|
-
|
|
4863
|
-
|
|
4864
|
-
|
|
4865
|
-
|
|
4866
|
-
|
|
4867
|
-
|
|
4868
|
-
|
|
4869
|
-
|
|
4870
|
-
|
|
4871
|
-
|
|
4872
|
-
|
|
4873
|
-
|
|
4874
|
-
|
|
4875
|
-
|
|
4876
|
-
|
|
4877
|
-
display: block;
|
|
4878
|
-
width: 100%;
|
|
4879
|
-
min-height: 50px;
|
|
4880
|
-
}
|
|
4881
|
-
</style>
|
|
4882
|
-
<script src="${this.requireJsUrl}"></script>
|
|
4883
|
-
<script>
|
|
4884
|
-
const forwardConsole = ${forwardConsole ? "true" : "false"};
|
|
4885
|
-
if (forwardConsole) {
|
|
4886
|
-
const originalLog = console.log.bind(console);
|
|
4887
|
-
const originalError = console.error.bind(console);
|
|
4888
|
-
const stringifyArgs = args =>
|
|
4889
|
-
args.map(arg => {
|
|
4890
|
-
if (typeof arg === 'string') return arg;
|
|
4891
|
-
try {
|
|
4892
|
-
return JSON.stringify(arg);
|
|
4893
|
-
} catch (e) {
|
|
4894
|
-
return String(arg);
|
|
4895
|
-
}
|
|
4896
|
-
});
|
|
4897
|
-
console.log = (...args) => {
|
|
4898
|
-
originalLog(...args);
|
|
4899
|
-
window.parent.postMessage(
|
|
4900
|
-
{
|
|
4901
|
-
source: 'qti-pci-iframe',
|
|
4902
|
-
responseIdentifier: (window.PCIManager && window.PCIManager.responseIdentifier) || null,
|
|
4903
|
-
method: 'console',
|
|
4904
|
-
level: 'log',
|
|
4905
|
-
args: stringifyArgs(args)
|
|
4906
|
-
},
|
|
4907
|
-
'*'
|
|
4908
|
-
);
|
|
4909
|
-
};
|
|
4910
|
-
console.error = (...args) => {
|
|
4911
|
-
originalError(...args);
|
|
4912
|
-
window.parent.postMessage(
|
|
4913
|
-
{
|
|
4914
|
-
source: 'qti-pci-iframe',
|
|
4915
|
-
responseIdentifier: (window.PCIManager && window.PCIManager.responseIdentifier) || null,
|
|
4916
|
-
method: 'console',
|
|
4917
|
-
level: 'error',
|
|
4918
|
-
args: stringifyArgs(args)
|
|
4919
|
-
},
|
|
4920
|
-
'*'
|
|
4921
|
-
);
|
|
4922
|
-
};
|
|
4923
|
-
}
|
|
4924
|
-
// Define standard paths and shims
|
|
4925
|
-
window.requirePaths = ${requirePaths};
|
|
4926
|
-
|
|
4927
|
-
window.requireShim = ${requireShim};
|
|
4928
|
-
|
|
4929
|
-
// Single initial RequireJS configuration with error handling
|
|
4930
|
-
window.requirejs.config({
|
|
4931
|
-
catchError: true,
|
|
4932
|
-
waitSeconds: 30,
|
|
4933
|
-
paths: window.requirePaths,
|
|
4934
|
-
baseUrl: '${iframeBaseUrl}',
|
|
4935
|
-
shim: window.requireShim,
|
|
4936
|
-
onNodeCreated: function(node, config, moduleName, url) {
|
|
4937
|
-
// Add error handler to script node
|
|
4938
|
-
node.addEventListener('error', function(evt) {
|
|
4939
|
-
console.error('Script load error for module:', moduleName, 'URL:', url, 'Event:', evt);
|
|
4940
|
-
});
|
|
4941
|
-
},
|
|
4942
|
-
onError: function(err) {
|
|
4943
|
-
console.error('RequireJS error:', {
|
|
4944
|
-
type: err.requireType,
|
|
4945
|
-
modules: err.requireModules,
|
|
4946
|
-
error: err
|
|
4947
|
-
});
|
|
4948
|
-
|
|
4949
|
-
if (err.requireType === 'scripterror') {
|
|
4950
|
-
console.error('Script error usually indicates a network or CORS issue with:', err.requireModules);
|
|
4951
|
-
}
|
|
4952
|
-
|
|
4953
|
-
// Notify parent window about the error
|
|
4954
|
-
window.parent.postMessage({
|
|
4955
|
-
source: 'qti-pci-iframe',
|
|
4956
|
-
responseIdentifier: (window.PCIManager && window.PCIManager.responseIdentifier) || null,
|
|
4957
|
-
method: 'error',
|
|
4958
|
-
params: {
|
|
4959
|
-
message: 'RequireJS ' + err.requireType + ' error for modules: ' + err.requireModules,
|
|
4960
|
-
details: {
|
|
4961
|
-
type: err.requireType,
|
|
4962
|
-
modules: err.requireModules,
|
|
4963
|
-
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%;
|
|
4964
4613
|
}
|
|
4965
|
-
|
|
4966
|
-
|
|
4967
|
-
}
|
|
4968
|
-
});
|
|
4969
|
-
|
|
4970
|
-
// PCI Manager for iframe implementation
|
|
4971
|
-
window.PCIManager = {
|
|
4972
|
-
pciInstance: null,
|
|
4973
|
-
container: null,
|
|
4974
|
-
markupEl: null,
|
|
4975
|
-
propertiesEl: null,
|
|
4976
|
-
customInteractionTypeIdentifier: null,
|
|
4977
|
-
responseIdentifier: null,
|
|
4978
|
-
pendingBoundTo: null,
|
|
4979
|
-
pendingMarkup: null,
|
|
4980
|
-
pendingProperties: null,
|
|
4981
|
-
pendingState: null,
|
|
4982
|
-
pendingStylesheets: null,
|
|
4983
|
-
stylesheetKeys: {},
|
|
4984
|
-
interactionChangedViaEvent: false,
|
|
4985
|
-
eventBridgeAttached: false,
|
|
4986
|
-
lastResponseStr: null,
|
|
4987
|
-
hadResponse: false,
|
|
4988
|
-
|
|
4989
|
-
initialize: function(config) {
|
|
4990
|
-
this.customInteractionTypeIdentifier = config.customInteractionTypeIdentifier;
|
|
4991
|
-
this.responseIdentifier = config.responseIdentifier;
|
|
4992
|
-
this.container = document.getElementById('pci-container');
|
|
4993
|
-
this.container.classList.add('qti-customInteraction');
|
|
4994
|
-
|
|
4995
|
-
function qtiVariableHasValue(qtiVar) {
|
|
4996
|
-
if (!qtiVar) return false;
|
|
4997
|
-
if (qtiVar.base) {
|
|
4998
|
-
for (const k in qtiVar.base) {
|
|
4999
|
-
if (!Object.prototype.hasOwnProperty.call(qtiVar.base, k)) continue;
|
|
5000
|
-
const v = qtiVar.base[k];
|
|
5001
|
-
if (v !== null && v !== undefined && v !== '') return true;
|
|
4614
|
+
#pci-container {
|
|
4615
|
+
width: 100%;
|
|
5002
4616
|
}
|
|
5003
|
-
|
|
5004
|
-
|
|
5005
|
-
|
|
5006
|
-
|
|
5007
|
-
const v = qtiVar.list[k];
|
|
5008
|
-
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;
|
|
5009
4621
|
}
|
|
5010
|
-
|
|
5011
|
-
|
|
5012
|
-
|
|
5013
|
-
|
|
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
|
+
}
|
|
5014
4694
|
|
|
5015
|
-
|
|
5016
|
-
|
|
5017
|
-
|
|
5018
|
-
|
|
5019
|
-
|
|
5020
|
-
|
|
5021
|
-
|
|
5022
|
-
|
|
5023
|
-
|
|
5024
|
-
|
|
5025
|
-
|
|
5026
|
-
|
|
5027
|
-
|
|
5028
|
-
|
|
5029
|
-
|
|
5030
|
-
|
|
5031
|
-
|
|
5032
|
-
|
|
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
|
+
});
|
|
5033
4714
|
|
|
5034
|
-
|
|
5035
|
-
|
|
5036
|
-
|
|
5037
|
-
|
|
5038
|
-
|
|
5039
|
-
|
|
5040
|
-
|
|
5041
|
-
|
|
5042
|
-
|
|
5043
|
-
|
|
5044
|
-
|
|
5045
|
-
|
|
5046
|
-
|
|
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
|
+
}
|
|
5047
4759
|
|
|
5048
|
-
|
|
5049
|
-
|
|
5050
|
-
|
|
5051
|
-
|
|
5052
|
-
|
|
5053
|
-
|
|
5054
|
-
|
|
5055
|
-
|
|
5056
|
-
|
|
5057
|
-
|
|
5058
|
-
|
|
5059
|
-
|
|
5060
|
-
|
|
5061
|
-
|
|
5062
|
-
|
|
5063
|
-
|
|
5064
|
-
|
|
5065
|
-
|
|
5066
|
-
true
|
|
5067
|
-
);
|
|
5068
|
-
}
|
|
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
|
+
}
|
|
5069
4778
|
|
|
5070
|
-
|
|
5071
|
-
|
|
5072
|
-
|
|
5073
|
-
|
|
5074
|
-
|
|
5075
|
-
|
|
5076
|
-
|
|
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
|
+
}
|
|
5077
4792
|
|
|
5078
|
-
|
|
5079
|
-
|
|
5080
|
-
|
|
5081
|
-
|
|
5082
|
-
|
|
5083
|
-
|
|
5084
|
-
|
|
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
|
+
}
|
|
5085
4817
|
|
|
5086
|
-
|
|
5087
|
-
|
|
5088
|
-
|
|
5089
|
-
|
|
5090
|
-
|
|
5091
|
-
|
|
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
|
+
}
|
|
5092
4825
|
|
|
5093
|
-
|
|
5094
|
-
|
|
5095
|
-
|
|
5096
|
-
|
|
5097
|
-
|
|
5098
|
-
|
|
5099
|
-
|
|
5100
|
-
const path2Array = Array.isArray(path2) ? path2 : [path2];
|
|
5101
|
-
return path1Array.concat(path2Array).filter((value, index, self) => self.indexOf(value) === index);
|
|
5102
|
-
}
|
|
4826
|
+
function removeDoubleSlashes(str) {
|
|
4827
|
+
return str
|
|
4828
|
+
.replace(/([^:\\/])\\/\\/+/g, '$1/')
|
|
4829
|
+
.replace(/\\/\\//g, '/')
|
|
4830
|
+
.replace('http:/', 'http://')
|
|
4831
|
+
.replace('https:/', 'https://');
|
|
4832
|
+
}
|
|
5103
4833
|
|
|
5104
|
-
// Update paths with modules from the config
|
|
5105
|
-
if (config.interactionModules && config.interactionModules.length > 0) {
|
|
5106
|
-
config.interactionModules.forEach(module => {
|
|
5107
|
-
if (module.id && module.primaryPath) {
|
|
5108
|
-
const currentPath = window.requirePaths[module.id] || [];
|
|
5109
|
-
const currentPaths = Array.isArray(currentPath) ? currentPath : [currentPath];
|
|
5110
|
-
const newPath = combineRequireResolvePaths(
|
|
5111
|
-
module.primaryPath, module.fallbackPath, config.baseUrl
|
|
5112
|
-
);
|
|
5113
|
-
window.requirePaths[module.id] = currentPaths.concat(newPath).filter((value, index, self) => self.indexOf(value) === index);
|
|
5114
|
-
}
|
|
5115
|
-
});
|
|
5116
|
-
}
|
|
5117
4834
|
|
|
5118
|
-
// The ONLY other requirejs.config call - with the context for this specific PCI
|
|
5119
|
-
window.requirejs.config({
|
|
5120
|
-
context: this.customInteractionTypeIdentifier,
|
|
5121
|
-
paths: window.requirePaths,
|
|
5122
|
-
shim: window.requireShim
|
|
5123
|
-
});
|
|
5124
4835
|
|
|
5125
|
-
|
|
5126
|
-
|
|
5127
|
-
|
|
5128
|
-
|
|
5129
|
-
|
|
5130
|
-
// Configure PCI instance
|
|
5131
|
-
const pciConfig = {
|
|
5132
|
-
properties: config.properties || {},
|
|
5133
|
-
contextVariables: config.contextVariables || {},
|
|
5134
|
-
templateVariables: config.templateVariables || {},
|
|
5135
|
-
onready: pciInstance => {
|
|
5136
|
-
this.pciInstance = pciInstance;
|
|
5137
|
-
// Apply any pending updates that arrived before onready
|
|
5138
|
-
if (this.pendingBoundTo) {
|
|
5139
|
-
this.applyBoundTo(this.pendingBoundTo);
|
|
5140
|
-
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;
|
|
5141
4841
|
}
|
|
5142
|
-
|
|
5143
|
-
|
|
5144
|
-
|
|
5145
|
-
|
|
5146
|
-
|
|
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
|
-
|
|
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
|
+
}
|
|
5194
4914
|
}
|
|
5195
|
-
|
|
5196
|
-
|
|
5197
|
-
|
|
5198
|
-
|
|
5199
|
-
|
|
5200
|
-
|
|
5201
|
-
|
|
5202
|
-
|
|
5203
|
-
|
|
5204
|
-
|
|
5205
|
-
|
|
5206
|
-
|
|
5207
|
-
|
|
5208
|
-
|
|
5209
|
-
|
|
5210
|
-
|
|
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();
|
|
5211
4974
|
}
|
|
5212
|
-
}
|
|
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}\`);
|
|
5213
4983
|
}
|
|
5214
|
-
return config;
|
|
5215
|
-
};
|
|
5216
|
-
const taoConfig = restoreTAOConfig(config.dataAttributes);
|
|
5217
4984
|
|
|
5218
|
-
|
|
5219
|
-
this.
|
|
5220
|
-
|
|
5221
|
-
Object.keys(taoConfig).length ? taoConfig : null
|
|
5222
|
-
);
|
|
5223
|
-
}
|
|
5224
|
-
},
|
|
5225
|
-
notifyReady: () => {
|
|
5226
|
-
PCIManager.notifyReady();
|
|
5227
|
-
}
|
|
5228
|
-
};
|
|
5229
|
-
});
|
|
4985
|
+
// Load the PCI module
|
|
4986
|
+
this.loadModule(config.module);
|
|
4987
|
+
},
|
|
5230
4988
|
|
|
5231
|
-
|
|
5232
|
-
|
|
5233
|
-
|
|
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
|
+
},
|
|
5234
5008
|
|
|
5235
|
-
|
|
5236
|
-
|
|
5237
|
-
|
|
5238
|
-
|
|
5239
|
-
|
|
5240
|
-
|
|
5241
|
-
|
|
5242
|
-
|
|
5243
|
-
|
|
5244
|
-
},
|
|
5245
|
-
console.error('Error loading module:', modulePath, err);
|
|
5246
|
-
this.notifyError('Module load error: ' + err.toString());
|
|
5247
|
-
});
|
|
5248
|
-
});
|
|
5249
|
-
} catch (error) {
|
|
5250
|
-
console.error('Exception in loadModule:', modulePath);
|
|
5251
|
-
console.error(error);
|
|
5252
|
-
this.notifyError('Error in require call: ' + error.toString());
|
|
5253
|
-
}
|
|
5254
|
-
},
|
|
5009
|
+
notifyReady: function () {
|
|
5010
|
+
window.parent.postMessage(
|
|
5011
|
+
{
|
|
5012
|
+
source: 'qti-pci-iframe',
|
|
5013
|
+
responseIdentifier: this.responseIdentifier,
|
|
5014
|
+
method: 'iframeReady'
|
|
5015
|
+
},
|
|
5016
|
+
'*'
|
|
5017
|
+
);
|
|
5018
|
+
},
|
|
5255
5019
|
|
|
5256
|
-
|
|
5257
|
-
|
|
5258
|
-
|
|
5259
|
-
|
|
5260
|
-
|
|
5261
|
-
|
|
5262
|
-
|
|
5263
|
-
|
|
5264
|
-
|
|
5265
|
-
|
|
5266
|
-
|
|
5267
|
-
responseIdentifier: this.responseIdentifier,
|
|
5268
|
-
method: 'interactionChanged',
|
|
5269
|
-
params: { value: response, state: state }
|
|
5270
|
-
}, '*');
|
|
5271
|
-
},
|
|
5272
|
-
|
|
5273
|
-
notifyError: function(message) {
|
|
5274
|
-
console.error('PCI Error:', message);
|
|
5275
|
-
window.parent.postMessage({
|
|
5276
|
-
source: 'qti-pci-iframe',
|
|
5277
|
-
responseIdentifier: this.responseIdentifier,
|
|
5278
|
-
method: 'error',
|
|
5279
|
-
params: { message: message }
|
|
5280
|
-
}, '*');
|
|
5281
|
-
},
|
|
5282
|
-
|
|
5283
|
-
setMarkup: function(markupHtml) {
|
|
5284
|
-
if (!this.container) {
|
|
5285
|
-
this.container = document.getElementById('pci-container');
|
|
5286
|
-
}
|
|
5287
|
-
if (!this.container) {
|
|
5288
|
-
this.pendingMarkup = markupHtml;
|
|
5289
|
-
return;
|
|
5290
|
-
}
|
|
5291
|
-
this.markupEl = this.container.querySelector('qti-interaction-markup');
|
|
5292
|
-
if (!this.markupEl) {
|
|
5293
|
-
this.markupEl = document.createElement('qti-interaction-markup');
|
|
5294
|
-
this.container.appendChild(this.markupEl);
|
|
5295
|
-
}
|
|
5296
|
-
this.markupEl.classList.add('qti-customInteraction');
|
|
5297
|
-
this.markupEl.innerHTML = markupHtml || '';
|
|
5298
|
-
},
|
|
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
|
+
},
|
|
5299
5031
|
|
|
5300
|
-
|
|
5301
|
-
|
|
5302
|
-
|
|
5303
|
-
|
|
5304
|
-
|
|
5305
|
-
|
|
5306
|
-
|
|
5307
|
-
|
|
5308
|
-
|
|
5309
|
-
|
|
5310
|
-
|
|
5311
|
-
|
|
5312
|
-
}
|
|
5313
|
-
this.propertiesEl.style.display = 'none';
|
|
5314
|
-
this.propertiesEl.innerHTML = propertiesHtml || '';
|
|
5315
|
-
},
|
|
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
|
+
},
|
|
5316
5044
|
|
|
5317
|
-
|
|
5318
|
-
|
|
5319
|
-
|
|
5320
|
-
|
|
5321
|
-
|
|
5322
|
-
|
|
5323
|
-
|
|
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
|
+
},
|
|
5324
5061
|
|
|
5325
|
-
|
|
5326
|
-
|
|
5327
|
-
|
|
5328
|
-
|
|
5329
|
-
|
|
5330
|
-
|
|
5331
|
-
|
|
5332
|
-
|
|
5333
|
-
|
|
5334
|
-
|
|
5335
|
-
|
|
5336
|
-
|
|
5337
|
-
|
|
5338
|
-
|
|
5339
|
-
|
|
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
|
+
},
|
|
5340
5078
|
|
|
5341
|
-
|
|
5342
|
-
|
|
5343
|
-
|
|
5344
|
-
|
|
5345
|
-
|
|
5346
|
-
|
|
5347
|
-
|
|
5348
|
-
this.injectStylesheet(sheet.content, key, scoped);
|
|
5349
|
-
return;
|
|
5350
|
-
}
|
|
5351
|
-
if (sheet.href) {
|
|
5352
|
-
fetch(sheet.href)
|
|
5353
|
-
.then(resp => resp.text())
|
|
5354
|
-
.then(css => this.injectStylesheet(css, key, scoped))
|
|
5355
|
-
.catch(() => {
|
|
5356
|
-
// ignore stylesheet load errors
|
|
5357
|
-
});
|
|
5358
|
-
}
|
|
5359
|
-
});
|
|
5360
|
-
},
|
|
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
|
+
},
|
|
5361
5086
|
|
|
5362
|
-
|
|
5363
|
-
|
|
5364
|
-
|
|
5365
|
-
|
|
5366
|
-
|
|
5367
|
-
|
|
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
|
+
},
|
|
5368
5096
|
|
|
5369
|
-
// Set up message listener for communication with parent
|
|
5370
|
-
let expectedParentOrigin = null;
|
|
5371
|
-
window.addEventListener('message', function(event) {
|
|
5372
|
-
const { data } = event;
|
|
5373
|
-
|
|
5374
|
-
// Ensure the message is from our parent
|
|
5375
|
-
if (event.source !== window.parent || !data || data.source !== 'qti-portable-custom-interaction') {
|
|
5376
|
-
return;
|
|
5377
|
-
}
|
|
5378
|
-
if (expectedParentOrigin === null) {
|
|
5379
|
-
expectedParentOrigin = event.origin;
|
|
5380
|
-
} else if (event.origin !== expectedParentOrigin) {
|
|
5381
|
-
return;
|
|
5382
|
-
}
|
|
5383
|
-
|
|
5384
|
-
function deepQuerySelector(root, selector) {
|
|
5385
|
-
if (!root) return null;
|
|
5386
|
-
try {
|
|
5387
|
-
const direct = root.querySelector ? root.querySelector(selector) : null;
|
|
5388
|
-
if (direct) return direct;
|
|
5389
|
-
} catch (e) {
|
|
5390
|
-
// ignore invalid selector for this root
|
|
5391
|
-
}
|
|
5392
|
-
if (!root.querySelectorAll) return null;
|
|
5393
|
-
const nodes = root.querySelectorAll('*');
|
|
5394
|
-
for (const node of nodes) {
|
|
5395
|
-
if (node && node.shadowRoot) {
|
|
5396
|
-
const found = deepQuerySelector(node.shadowRoot, selector);
|
|
5397
|
-
if (found) return found;
|
|
5398
|
-
}
|
|
5399
|
-
}
|
|
5400
|
-
return null;
|
|
5401
|
-
}
|
|
5402
5097
|
|
|
5403
|
-
|
|
5404
|
-
|
|
5405
|
-
|
|
5406
|
-
|
|
5407
|
-
|
|
5408
|
-
|
|
5409
|
-
if (node.shadowRoot) {
|
|
5410
|
-
const found = deepFindElementByExactText(node.shadowRoot, text);
|
|
5411
|
-
if (found) return found;
|
|
5412
|
-
}
|
|
5413
|
-
}
|
|
5414
|
-
}
|
|
5415
|
-
return null;
|
|
5416
|
-
}
|
|
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
|
+
};
|
|
5417
5104
|
|
|
5418
|
-
|
|
5419
|
-
|
|
5420
|
-
|
|
5421
|
-
|
|
5105
|
+
// Set up message listener for communication with parent
|
|
5106
|
+
let expectedParentOrigin = null;
|
|
5107
|
+
window.addEventListener('message', function (event) {
|
|
5108
|
+
const { data } = event;
|
|
5422
5109
|
|
|
5423
|
-
|
|
5424
|
-
|
|
5425
|
-
|
|
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
|
+
}
|
|
5426
5119
|
|
|
5427
|
-
|
|
5428
|
-
|
|
5429
|
-
|
|
5430
|
-
|
|
5431
|
-
|
|
5432
|
-
|
|
5433
|
-
|
|
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
|
+
}
|
|
5434
5138
|
|
|
5435
|
-
|
|
5436
|
-
|
|
5437
|
-
|
|
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
|
+
}
|
|
5438
5153
|
|
|
5439
|
-
|
|
5440
|
-
|
|
5441
|
-
|
|
5154
|
+
switch (data.method) {
|
|
5155
|
+
case 'initialize':
|
|
5156
|
+
PCIManager.initialize(data.params);
|
|
5157
|
+
break;
|
|
5442
5158
|
|
|
5443
|
-
|
|
5444
|
-
|
|
5445
|
-
|
|
5446
|
-
} else {
|
|
5447
|
-
PCIManager.pendingState = (data.params && data.params.state) || data.params;
|
|
5448
|
-
}
|
|
5449
|
-
break;
|
|
5159
|
+
case 'setMarkup':
|
|
5160
|
+
PCIManager.setMarkup(data.params);
|
|
5161
|
+
break;
|
|
5450
5162
|
|
|
5451
|
-
|
|
5452
|
-
|
|
5453
|
-
|
|
5454
|
-
|
|
5455
|
-
|
|
5456
|
-
|
|
5457
|
-
|
|
5458
|
-
if (node && node.shadowRoot) {
|
|
5459
|
-
parts.push(node.shadowRoot.innerHTML || '');
|
|
5460
|
-
parts.push(...collectShadowHtml(node.shadowRoot));
|
|
5461
|
-
}
|
|
5462
|
-
}
|
|
5463
|
-
return parts;
|
|
5464
|
-
};
|
|
5465
|
-
const shadowHtml = collectShadowHtml(document).join('\\n');
|
|
5466
|
-
window.parent.postMessage(
|
|
5467
|
-
{
|
|
5468
|
-
source: 'qti-pci-iframe',
|
|
5469
|
-
responseIdentifier: PCIManager.responseIdentifier,
|
|
5470
|
-
method: 'getContentResponse',
|
|
5471
|
-
messageId: messageId,
|
|
5472
|
-
content: (document.documentElement ? document.documentElement.outerHTML : '') + '\\n' + shadowHtml
|
|
5473
|
-
},
|
|
5474
|
-
'*'
|
|
5475
|
-
);
|
|
5476
|
-
break;
|
|
5477
|
-
}
|
|
5163
|
+
case 'setBoundTo':
|
|
5164
|
+
if (PCIManager.pciInstance) {
|
|
5165
|
+
PCIManager.applyBoundTo(data.params);
|
|
5166
|
+
} else {
|
|
5167
|
+
PCIManager.pendingBoundTo = data.params;
|
|
5168
|
+
}
|
|
5169
|
+
break;
|
|
5478
5170
|
|
|
5479
|
-
|
|
5480
|
-
|
|
5481
|
-
|
|
5482
|
-
|
|
5483
|
-
|
|
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
|
-
case 'getBoundingRect': {
|
|
5509
|
-
const messageId = data.params && data.params.messageId;
|
|
5510
|
-
const selector = data.params && data.params.selector;
|
|
5511
|
-
const el = selector ? deepQuerySelector(document, selector) : null;
|
|
5512
|
-
const rect = el && typeof el.getBoundingClientRect === 'function' ? el.getBoundingClientRect() : null;
|
|
5513
|
-
window.parent.postMessage(
|
|
5514
|
-
{
|
|
5515
|
-
source: 'qti-pci-iframe',
|
|
5516
|
-
responseIdentifier: PCIManager.responseIdentifier,
|
|
5517
|
-
method: 'getBoundingRectResponse',
|
|
5518
|
-
messageId: messageId,
|
|
5519
|
-
rect: rect
|
|
5520
|
-
? {
|
|
5521
|
-
left: rect.left,
|
|
5522
|
-
top: rect.top,
|
|
5523
|
-
width: rect.width,
|
|
5524
|
-
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
|
+
}
|
|
5525
5198
|
}
|
|
5526
|
-
|
|
5527
|
-
|
|
5528
|
-
|
|
5529
|
-
|
|
5530
|
-
|
|
5531
|
-
|
|
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
|
+
}
|
|
5532
5214
|
|
|
5533
|
-
|
|
5534
|
-
|
|
5535
|
-
|
|
5536
|
-
|
|
5537
|
-
|
|
5538
|
-
|
|
5539
|
-
|
|
5540
|
-
|
|
5541
|
-
|
|
5542
|
-
|
|
5543
|
-
|
|
5544
|
-
|
|
5545
|
-
|
|
5546
|
-
|
|
5547
|
-
|
|
5548
|
-
|
|
5549
|
-
|
|
5550
|
-
|
|
5551
|
-
|
|
5552
|
-
|
|
5553
|
-
|
|
5554
|
-
|
|
5555
|
-
|
|
5556
|
-
|
|
5557
|
-
|
|
5558
|
-
|
|
5559
|
-
|
|
5560
|
-
|
|
5561
|
-
responseIdentifier: PCIManager.responseIdentifier,
|
|
5562
|
-
method: 'clickTextResponse',
|
|
5563
|
-
messageId: messageId,
|
|
5564
|
-
success: success
|
|
5565
|
-
},
|
|
5566
|
-
'*'
|
|
5567
|
-
);
|
|
5568
|
-
break;
|
|
5569
|
-
}
|
|
5570
|
-
|
|
5571
|
-
case 'setValueElement': {
|
|
5572
|
-
const messageId = data.params && data.params.messageId;
|
|
5573
|
-
const selector = data.params && data.params.selector;
|
|
5574
|
-
const value = data.params && data.params.value;
|
|
5575
|
-
const el = selector ? deepQuerySelector(document, selector) : null;
|
|
5576
|
-
let success = false;
|
|
5577
|
-
if (el && 'value' in el) {
|
|
5578
|
-
try {
|
|
5579
|
-
el.value = value;
|
|
5580
|
-
el.dispatchEvent(new Event('input', { bubbles: true }));
|
|
5581
|
-
el.dispatchEvent(new Event('change', { bubbles: true }));
|
|
5582
|
-
success = true;
|
|
5583
|
-
} catch (e) {
|
|
5584
|
-
success = false;
|
|
5585
|
-
}
|
|
5586
|
-
}
|
|
5587
|
-
window.parent.postMessage(
|
|
5588
|
-
{
|
|
5589
|
-
source: 'qti-pci-iframe',
|
|
5590
|
-
responseIdentifier: PCIManager.responseIdentifier,
|
|
5591
|
-
method: 'setValueResponse',
|
|
5592
|
-
messageId: messageId,
|
|
5593
|
-
success: success
|
|
5594
|
-
},
|
|
5595
|
-
'*'
|
|
5596
|
-
);
|
|
5597
|
-
break;
|
|
5598
|
-
}
|
|
5599
|
-
|
|
5600
|
-
case 'console':
|
|
5601
|
-
window.parent.postMessage(
|
|
5602
|
-
{
|
|
5603
|
-
source: 'qti-pci-iframe',
|
|
5604
|
-
responseIdentifier: PCIManager.responseIdentifier,
|
|
5605
|
-
method: 'console',
|
|
5606
|
-
level: data.level,
|
|
5607
|
-
args: data.args
|
|
5608
|
-
},
|
|
5609
|
-
'*'
|
|
5610
|
-
);
|
|
5611
|
-
break;
|
|
5612
|
-
}
|
|
5613
|
-
});
|
|
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
|
+
}
|
|
5614
5243
|
|
|
5615
|
-
|
|
5616
|
-
|
|
5617
|
-
|
|
5618
|
-
|
|
5619
|
-
|
|
5620
|
-
|
|
5621
|
-
|
|
5622
|
-
|
|
5623
|
-
|
|
5624
|
-
|
|
5625
|
-
|
|
5626
|
-
|
|
5627
|
-
|
|
5628
|
-
|
|
5629
|
-
|
|
5630
|
-
|
|
5631
|
-
|
|
5632
|
-
|
|
5633
|
-
|
|
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
|
+
}
|
|
5634
5268
|
|
|
5635
|
-
|
|
5636
|
-
|
|
5637
|
-
|
|
5638
|
-
|
|
5639
|
-
|
|
5640
|
-
|
|
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
|
+
}
|
|
5641
5287
|
|
|
5642
|
-
|
|
5643
|
-
|
|
5644
|
-
|
|
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
|
+
}
|
|
5645
5306
|
|
|
5646
|
-
|
|
5647
|
-
|
|
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
|
+
}
|
|
5648
5335
|
|
|
5649
|
-
|
|
5650
|
-
|
|
5651
|
-
|
|
5652
|
-
|
|
5653
|
-
|
|
5654
|
-
|
|
5655
|
-
|
|
5656
|
-
|
|
5657
|
-
|
|
5658
|
-
|
|
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
|
+
});
|
|
5659
5350
|
|
|
5660
|
-
|
|
5661
|
-
|
|
5662
|
-
|
|
5663
|
-
|
|
5664
|
-
|
|
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
|
-
|
|
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
|
+
);
|
|
5710
5456
|
}
|
|
5711
5457
|
/**
|
|
5712
5458
|
* Toggle the display of the correct response
|
|
@@ -7078,17 +6824,23 @@ var QtiHottext = class extends ActiveElementMixin(i2, "qti-hottext") {
|
|
|
7078
6824
|
}
|
|
7079
6825
|
};
|
|
7080
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
|
+
|
|
7081
6840
|
// ../qti-interactions/src/elements/qti-inline-choice/qti-inline-choice.ts
|
|
7082
|
-
var QtiInlineChoice = class extends i2 {
|
|
7083
|
-
static
|
|
7084
|
-
|
|
7085
|
-
i`
|
|
7086
|
-
:host {
|
|
7087
|
-
display: block;
|
|
7088
|
-
cursor: pointer;
|
|
7089
|
-
}
|
|
7090
|
-
`
|
|
7091
|
-
];
|
|
6841
|
+
var QtiInlineChoice = class extends ActiveElementMixin(i2, "qti-inline-choice") {
|
|
6842
|
+
static {
|
|
6843
|
+
this.styles = qti_inline_choice_styles_default;
|
|
7092
6844
|
}
|
|
7093
6845
|
connectedCallback() {
|
|
7094
6846
|
super.connectedCallback();
|
|
@@ -7852,4 +7604,4 @@ lit-html/node/directives/ref.js:
|
|
|
7852
7604
|
* SPDX-License-Identifier: BSD-3-Clause
|
|
7853
7605
|
*)
|
|
7854
7606
|
*/
|
|
7855
|
-
//# sourceMappingURL=chunk-
|
|
7607
|
+
//# sourceMappingURL=chunk-2EVAV2SS.js.map
|