@citolab/qti-components 7.3.23 → 7.4.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 +5485 -1
- package/custom-elements.json +29837 -0
- package/dist/chunks/chunk-6K4ROGDD.js +364 -0
- package/dist/chunks/chunk-6K4ROGDD.js.map +1 -0
- package/dist/chunks/chunk-GUDRSXIP.js +189 -0
- package/dist/chunks/chunk-GUDRSXIP.js.map +1 -0
- package/dist/chunks/{chunk-WFUXZ4UT.js → chunk-JDY5GL7E.js} +13 -1
- package/dist/chunks/{chunk-WFUXZ4UT.js.map → chunk-JDY5GL7E.js.map} +1 -1
- package/dist/chunks/{chunk-LF6SO3JU.js → chunk-LN74Z5QM.js} +653 -450
- package/dist/chunks/chunk-LN74Z5QM.js.map +1 -0
- package/dist/chunks/{chunk-ELDMXTUQ.js → chunk-O5N4DCU6.js} +7 -2
- package/dist/chunks/chunk-O5N4DCU6.js.map +1 -0
- package/dist/chunks/chunk-TJ6ZOT7A.js +2235 -0
- package/dist/chunks/chunk-TJ6ZOT7A.js.map +1 -0
- package/dist/computed-item.context-DpK-sS0T.d.ts +19 -0
- package/dist/{exports/config.context.d.ts → config.context-CmQ9L62n.d.ts} +1 -4
- package/dist/custom-elements.json +29837 -0
- package/dist/index.d.ts +8 -39
- package/dist/index.js +34 -104
- package/{cdn/chunks/chunk-5TDBMEY5.js → dist/item.css} +7 -24
- package/dist/qti-assessment-item-D77dJ1Ot.d.ts +104 -0
- package/dist/qti-components/index.d.ts +1238 -19
- package/dist/qti-components/index.js +6 -14
- package/dist/qti-components-jsx.d.ts +4463 -0
- package/dist/qti-item/index.d.ts +113 -0
- package/dist/qti-item/index.js +16 -0
- package/dist/qti-loader/index.d.ts +2 -6
- package/dist/qti-loader/index.js +24 -5
- package/dist/qti-loader/index.js.map +1 -1
- package/dist/qti-test/index.d.ts +593 -0
- package/dist/qti-test/index.js +62 -0
- package/dist/qti-transformers/index.d.ts +38 -2
- package/dist/qti-transformers/index.js +1 -2
- package/dist/test.context-L609DNAz.d.ts +58 -0
- package/dist/variables-Vgyr6yyW.d.ts +58 -0
- package/dist/vscode.css-custom-data.json +11 -0
- package/dist/vscode.html-custom-data.json +1135 -0
- package/package.json +74 -116
- package/cdn/chunks/chunk-2BSPQRNR.js +0 -6
- package/cdn/chunks/chunk-3CVBBZDT.js +0 -17
- package/cdn/chunks/chunk-3IBGLVHA.js +0 -1
- package/cdn/chunks/chunk-3YQMAZEO.js +0 -29
- package/cdn/chunks/chunk-5UQLGV7F.js +0 -5
- package/cdn/chunks/chunk-5WLHU3FH.js +0 -8
- package/cdn/chunks/chunk-6SGFGN34.js +0 -8
- package/cdn/chunks/chunk-6XVY32RS.js +0 -1
- package/cdn/chunks/chunk-7ME56ODO.js +0 -1
- package/cdn/chunks/chunk-CZDMOYYF.js +0 -1
- package/cdn/chunks/chunk-DNBBQ7LY.js +0 -1
- package/cdn/chunks/chunk-DP5FVJRS.js +0 -21
- package/cdn/chunks/chunk-E6IOJ4MV.js +0 -23
- package/cdn/chunks/chunk-G76Y52EL.js +0 -1471
- package/cdn/chunks/chunk-HCXHHI6V.js +0 -18
- package/cdn/chunks/chunk-HDMF4QZO.js +0 -48
- package/cdn/chunks/chunk-HKWYQB5Z.js +0 -37
- package/cdn/chunks/chunk-J5P2GBPV.js +0 -1
- package/cdn/chunks/chunk-KWPDTFYH.js +0 -1
- package/cdn/chunks/chunk-LIUOA3YV.js +0 -1
- package/cdn/chunks/chunk-LO2NM3CE.js +0 -1
- package/cdn/chunks/chunk-MD33BNWM.js +0 -1
- package/cdn/chunks/chunk-MRQ46JRY.js +0 -8
- package/cdn/chunks/chunk-NJP4VZB3.js +0 -8
- package/cdn/chunks/chunk-O4RAJ3LM.js +0 -25
- package/cdn/chunks/chunk-OSLUHYFD.js +0 -8
- package/cdn/chunks/chunk-P6SSTAJ2.js +0 -1
- package/cdn/chunks/chunk-PP62N3C4.js +0 -8
- package/cdn/chunks/chunk-PU7OABT3.js +0 -1
- package/cdn/chunks/chunk-PUI3LIFH.js +0 -0
- package/cdn/chunks/chunk-QBAXWCYQ.js +0 -5
- package/cdn/chunks/chunk-QEFO63QX.js +0 -8
- package/cdn/chunks/chunk-QQCGUVEV.js +0 -1
- package/cdn/chunks/chunk-QQR6POPY.js +0 -1
- package/cdn/chunks/chunk-RGNFAPWY.js +0 -2
- package/cdn/chunks/chunk-S5QDLZ6J.js +0 -1
- package/cdn/chunks/chunk-SEM2PEP6.js +0 -5
- package/cdn/chunks/chunk-SVVO2SFS.js +0 -1
- package/cdn/chunks/chunk-TCHROQU3.js +0 -1
- package/cdn/chunks/chunk-UMDZKG6K.js +0 -1
- package/cdn/chunks/chunk-VEG7LKS3.js +0 -48
- package/cdn/chunks/chunk-WCCTDLQ4.js +0 -8
- package/cdn/chunks/chunk-X4FA36TC.js +0 -1
- package/cdn/chunks/chunk-XJ2CFTZP.js +0 -1
- package/cdn/chunks/chunk-XUJ7TXHW.js +0 -1
- package/cdn/chunks/chunk-YCBNF5QU.js +0 -17
- package/cdn/chunks/chunk-YHLQ2JQ2.js +0 -1
- package/cdn/chunks/chunk-ZPCQH3EY.js +0 -10
- package/cdn/exports/computed-item.context.js +0 -1
- package/cdn/exports/computed.context.js +0 -1
- package/cdn/exports/config.context.js +0 -1
- package/cdn/exports/expression-result.js +0 -0
- package/cdn/exports/interaction.interface.js +0 -0
- package/cdn/exports/interaction.js +0 -1
- package/cdn/exports/item.context.js +0 -1
- package/cdn/exports/qti-assessment-item.context.js +0 -1
- package/cdn/exports/qti-condition-expression.js +0 -1
- package/cdn/exports/qti-expression.js +0 -1
- package/cdn/exports/qti-test.js +0 -0
- package/cdn/exports/qti.context.js +0 -1
- package/cdn/exports/session.context.js +0 -1
- package/cdn/exports/test.context.js +0 -1
- package/cdn/exports/variables.js +0 -0
- package/cdn/qti-components/index.js +0 -1
- package/cdn/qti-item/components/item-container.js +0 -1
- package/cdn/qti-item/components/item-correct-response-mode.js +0 -10
- package/cdn/qti-item/components/item-show-candidate-correction.js +0 -8
- package/cdn/qti-item/components/item-show-correct-response.js +0 -1
- package/cdn/qti-item/components/print-item-variables.js +0 -1
- package/cdn/qti-item/components/styles.js +0 -1
- package/cdn/qti-item/core/index.js +0 -1
- package/cdn/qti-loader/index.js +0 -1
- package/cdn/qti-test/components/index.js +0 -1
- package/cdn/qti-test/components/styles.js +0 -1
- package/cdn/qti-test/components/test-check-item.js +0 -1
- package/cdn/qti-test/components/test-container.js +0 -1
- package/cdn/qti-test/components/test-end-attempt.js +0 -1
- package/cdn/qti-test/components/test-item-link.js +0 -1
- package/cdn/qti-test/components/test-navigation.js +0 -1
- package/cdn/qti-test/components/test-next.js +0 -1
- package/cdn/qti-test/components/test-paging-buttons-stamp.js +0 -1
- package/cdn/qti-test/components/test-prev.js +0 -1
- package/cdn/qti-test/components/test-print-context.js +0 -1
- package/cdn/qti-test/components/test-print-item-variables.js +0 -1
- package/cdn/qti-test/components/test-scoring-buttons.js +0 -1
- package/cdn/qti-test/components/test-scoring-feedback.js +0 -1
- package/cdn/qti-test/components/test-section-buttons-stamp.js +0 -1
- package/cdn/qti-test/components/test-section-link.js +0 -1
- package/cdn/qti-test/components/test-show-correct-response.js +0 -1
- package/cdn/qti-test/components/test-stamp.js +0 -1
- package/cdn/qti-test/components/test-view-toggle.js +0 -1
- package/cdn/qti-test/components/test-view.js +0 -1
- package/cdn/qti-test/core/index.js +0 -1
- package/cdn/qti-transformers/index.js +0 -1
- package/dist/chunks/chunk-22IRJWWY.js +0 -10
- package/dist/chunks/chunk-22IRJWWY.js.map +0 -1
- package/dist/chunks/chunk-36G5MQKR.js +0 -85
- package/dist/chunks/chunk-36G5MQKR.js.map +0 -1
- package/dist/chunks/chunk-4OGJBG35.js +0 -8
- package/dist/chunks/chunk-4OGJBG35.js.map +0 -1
- package/dist/chunks/chunk-5KCXO2RP.js +0 -36
- package/dist/chunks/chunk-5KCXO2RP.js.map +0 -1
- package/dist/chunks/chunk-5ZHHNEDA.js +0 -30
- package/dist/chunks/chunk-5ZHHNEDA.js.map +0 -1
- package/dist/chunks/chunk-7K44TDQO.js +0 -91
- package/dist/chunks/chunk-7K44TDQO.js.map +0 -1
- package/dist/chunks/chunk-7OMJMXHK.js +0 -50
- package/dist/chunks/chunk-7OMJMXHK.js.map +0 -1
- package/dist/chunks/chunk-AZIKAG7K.js +0 -8
- package/dist/chunks/chunk-AZIKAG7K.js.map +0 -1
- package/dist/chunks/chunk-BLBSZDQ4.js +0 -52
- package/dist/chunks/chunk-BLBSZDQ4.js.map +0 -1
- package/dist/chunks/chunk-CJADUWEC.js +0 -10
- package/dist/chunks/chunk-CJADUWEC.js.map +0 -1
- package/dist/chunks/chunk-CP34TICQ.js +0 -45
- package/dist/chunks/chunk-CP34TICQ.js.map +0 -1
- package/dist/chunks/chunk-CTESMSQO.js +0 -56
- package/dist/chunks/chunk-CTESMSQO.js.map +0 -1
- package/dist/chunks/chunk-DZAMXOSC.js +0 -30
- package/dist/chunks/chunk-DZAMXOSC.js.map +0 -1
- package/dist/chunks/chunk-E6V3JHVT.js +0 -31
- package/dist/chunks/chunk-E6V3JHVT.js.map +0 -1
- package/dist/chunks/chunk-EJZQSOHU.js +0 -85
- package/dist/chunks/chunk-EJZQSOHU.js.map +0 -1
- package/dist/chunks/chunk-ELDMXTUQ.js.map +0 -1
- package/dist/chunks/chunk-EMVOKXSA.js +0 -84
- package/dist/chunks/chunk-EMVOKXSA.js.map +0 -1
- package/dist/chunks/chunk-ERYHQVOT.js +0 -8
- package/dist/chunks/chunk-ERYHQVOT.js.map +0 -1
- package/dist/chunks/chunk-ETLOKJAG.js +0 -94
- package/dist/chunks/chunk-ETLOKJAG.js.map +0 -1
- package/dist/chunks/chunk-FL72PF4D.js +0 -19
- package/dist/chunks/chunk-FL72PF4D.js.map +0 -1
- package/dist/chunks/chunk-GAHXUFMQ.js +0 -93
- package/dist/chunks/chunk-GAHXUFMQ.js.map +0 -1
- package/dist/chunks/chunk-H2JE6IVU.js +0 -15
- package/dist/chunks/chunk-H2JE6IVU.js.map +0 -1
- package/dist/chunks/chunk-H6KHXSIO.js +0 -8
- package/dist/chunks/chunk-H6KHXSIO.js.map +0 -1
- package/dist/chunks/chunk-HFAUM56X.js +0 -208
- package/dist/chunks/chunk-HFAUM56X.js.map +0 -1
- package/dist/chunks/chunk-JQ6HWGRY.js +0 -22
- package/dist/chunks/chunk-JQ6HWGRY.js.map +0 -1
- package/dist/chunks/chunk-KG5Z2CKO.js +0 -86
- package/dist/chunks/chunk-KG5Z2CKO.js.map +0 -1
- package/dist/chunks/chunk-KSXNC564.js +0 -43
- package/dist/chunks/chunk-KSXNC564.js.map +0 -1
- package/dist/chunks/chunk-LF6SO3JU.js.map +0 -1
- package/dist/chunks/chunk-NJNQOQUU.js +0 -8
- package/dist/chunks/chunk-NJNQOQUU.js.map +0 -1
- package/dist/chunks/chunk-ODHS7HDB.js +0 -94
- package/dist/chunks/chunk-ODHS7HDB.js.map +0 -1
- package/dist/chunks/chunk-QCB6P7DH.js +0 -146
- package/dist/chunks/chunk-QCB6P7DH.js.map +0 -1
- package/dist/chunks/chunk-QU7KR7VX.js +0 -367
- package/dist/chunks/chunk-QU7KR7VX.js.map +0 -1
- package/dist/chunks/chunk-TU6COU44.js +0 -82
- package/dist/chunks/chunk-TU6COU44.js.map +0 -1
- package/dist/chunks/chunk-TUKEQ36K.js +0 -60
- package/dist/chunks/chunk-TUKEQ36K.js.map +0 -1
- package/dist/chunks/chunk-TWYN56XO.js +0 -92
- package/dist/chunks/chunk-TWYN56XO.js.map +0 -1
- package/dist/chunks/chunk-UHQVQBCL.js +0 -89
- package/dist/chunks/chunk-UHQVQBCL.js.map +0 -1
- package/dist/chunks/chunk-WFFIUFJJ.js +0 -28
- package/dist/chunks/chunk-WFFIUFJJ.js.map +0 -1
- package/dist/chunks/chunk-WN6TJQI2.js +0 -103
- package/dist/chunks/chunk-WN6TJQI2.js.map +0 -1
- package/dist/chunks/chunk-WPEFNKMC.js +0 -923
- package/dist/chunks/chunk-WPEFNKMC.js.map +0 -1
- package/dist/chunks/chunk-YPMZLHGG.js +0 -52
- package/dist/chunks/chunk-YPMZLHGG.js.map +0 -1
- package/dist/chunks/chunk-YWEWSQJR.js +0 -40
- package/dist/chunks/chunk-YWEWSQJR.js.map +0 -1
- package/dist/chunks/chunk-ZGSNDSK3.js +0 -1
- package/dist/chunks/chunk-ZGSNDSK3.js.map +0 -1
- package/dist/exports/computed-item.context.d.ts +0 -29
- package/dist/exports/computed-item.context.js +0 -8
- package/dist/exports/computed-item.context.js.map +0 -1
- package/dist/exports/computed.context.d.ts +0 -42
- package/dist/exports/computed.context.js +0 -8
- package/dist/exports/computed.context.js.map +0 -1
- package/dist/exports/config.context.js +0 -8
- package/dist/exports/config.context.js.map +0 -1
- package/dist/exports/expression-result.d.ts +0 -19
- package/dist/exports/expression-result.js +0 -1
- package/dist/exports/expression-result.js.map +0 -1
- package/dist/exports/interaction.d.ts +0 -8
- package/dist/exports/interaction.interface.d.ts +0 -13
- package/dist/exports/interaction.interface.js +0 -1
- package/dist/exports/interaction.interface.js.map +0 -1
- package/dist/exports/interaction.js +0 -12
- package/dist/exports/interaction.js.map +0 -1
- package/dist/exports/item.context.d.ts +0 -8
- package/dist/exports/item.context.js +0 -8
- package/dist/exports/item.context.js.map +0 -1
- package/dist/exports/qti-assessment-item.context.d.ts +0 -14
- package/dist/exports/qti-assessment-item.context.js +0 -8
- package/dist/exports/qti-assessment-item.context.js.map +0 -1
- package/dist/exports/qti-condition-expression.d.ts +0 -15
- package/dist/exports/qti-condition-expression.js +0 -11
- package/dist/exports/qti-condition-expression.js.map +0 -1
- package/dist/exports/qti-expression.d.ts +0 -8
- package/dist/exports/qti-expression.js +0 -10
- package/dist/exports/qti-expression.js.map +0 -1
- package/dist/exports/qti-test.d.ts +0 -40
- package/dist/exports/qti-test.js +0 -1
- package/dist/exports/qti-test.js.map +0 -1
- package/dist/exports/qti.context.d.ts +0 -14
- package/dist/exports/qti.context.js +0 -8
- package/dist/exports/qti.context.js.map +0 -1
- package/dist/exports/session.context.d.ts +0 -26
- package/dist/exports/session.context.js +0 -10
- package/dist/exports/session.context.js.map +0 -1
- package/dist/exports/test.context.d.ts +0 -21
- package/dist/exports/test.context.js +0 -10
- package/dist/exports/test.context.js.map +0 -1
- package/dist/exports/variables.d.ts +0 -8
- package/dist/exports/variables.js +0 -1
- package/dist/exports/variables.js.map +0 -1
- package/dist/qti-item/components/item-container.d.ts +0 -40
- package/dist/qti-item/components/item-container.js +0 -11
- package/dist/qti-item/components/item-container.js.map +0 -1
- package/dist/qti-item/components/item-correct-response-mode.d.ts +0 -17
- package/dist/qti-item/components/item-correct-response-mode.js +0 -62
- package/dist/qti-item/components/item-correct-response-mode.js.map +0 -1
- package/dist/qti-item/components/item-show-candidate-correction.d.ts +0 -34
- package/dist/qti-item/components/item-show-candidate-correction.js +0 -88
- package/dist/qti-item/components/item-show-candidate-correction.js.map +0 -1
- package/dist/qti-item/components/item-show-correct-response.d.ts +0 -33
- package/dist/qti-item/components/item-show-correct-response.js +0 -10
- package/dist/qti-item/components/item-show-correct-response.js.map +0 -1
- package/dist/qti-item/components/print-item-variables.d.ts +0 -23
- package/dist/qti-item/components/print-item-variables.js +0 -9
- package/dist/qti-item/components/print-item-variables.js.map +0 -1
- package/dist/qti-item/components/styles.d.ts +0 -8
- package/dist/qti-item/components/styles.js +0 -14
- package/dist/qti-item/components/styles.js.map +0 -1
- package/dist/qti-item/core/index.d.ts +0 -51
- package/dist/qti-item/core/index.js +0 -26
- package/dist/qti-response-declaration-DKr08ANy.d.ts +0 -1397
- package/dist/qti-test/components/index.d.ts +0 -33
- package/dist/qti-test/components/index.js +0 -87
- package/dist/qti-test/components/styles.d.ts +0 -8
- package/dist/qti-test/components/styles.js +0 -14
- package/dist/qti-test/components/styles.js.map +0 -1
- package/dist/qti-test/components/test-check-item.d.ts +0 -16
- package/dist/qti-test/components/test-check-item.js +0 -9
- package/dist/qti-test/components/test-check-item.js.map +0 -1
- package/dist/qti-test/components/test-container.d.ts +0 -39
- package/dist/qti-test/components/test-container.js +0 -11
- package/dist/qti-test/components/test-container.js.map +0 -1
- package/dist/qti-test/components/test-end-attempt.d.ts +0 -16
- package/dist/qti-test/components/test-end-attempt.js +0 -9
- package/dist/qti-test/components/test-end-attempt.js.map +0 -1
- package/dist/qti-test/components/test-item-link.d.ts +0 -18
- package/dist/qti-test/components/test-item-link.js +0 -9
- package/dist/qti-test/components/test-item-link.js.map +0 -1
- package/dist/qti-test/components/test-navigation.d.ts +0 -73
- package/dist/qti-test/components/test-navigation.js +0 -13
- package/dist/qti-test/components/test-navigation.js.map +0 -1
- package/dist/qti-test/components/test-next.d.ts +0 -48
- package/dist/qti-test/components/test-next.js +0 -12
- package/dist/qti-test/components/test-next.js.map +0 -1
- package/dist/qti-test/components/test-paging-buttons-stamp.d.ts +0 -23
- package/dist/qti-test/components/test-paging-buttons-stamp.js +0 -9
- package/dist/qti-test/components/test-paging-buttons-stamp.js.map +0 -1
- package/dist/qti-test/components/test-prev.d.ts +0 -46
- package/dist/qti-test/components/test-prev.js +0 -12
- package/dist/qti-test/components/test-prev.js.map +0 -1
- package/dist/qti-test/components/test-print-context.d.ts +0 -25
- package/dist/qti-test/components/test-print-context.js +0 -9
- package/dist/qti-test/components/test-print-context.js.map +0 -1
- package/dist/qti-test/components/test-print-item-variables.d.ts +0 -27
- package/dist/qti-test/components/test-print-item-variables.js +0 -9
- package/dist/qti-test/components/test-print-item-variables.js.map +0 -1
- package/dist/qti-test/components/test-scoring-buttons.d.ts +0 -32
- package/dist/qti-test/components/test-scoring-buttons.js +0 -9
- package/dist/qti-test/components/test-scoring-buttons.js.map +0 -1
- package/dist/qti-test/components/test-scoring-feedback.d.ts +0 -27
- package/dist/qti-test/components/test-scoring-feedback.js +0 -9
- package/dist/qti-test/components/test-scoring-feedback.js.map +0 -1
- package/dist/qti-test/components/test-section-buttons-stamp.d.ts +0 -23
- package/dist/qti-test/components/test-section-buttons-stamp.js +0 -9
- package/dist/qti-test/components/test-section-buttons-stamp.js.map +0 -1
- package/dist/qti-test/components/test-section-link.d.ts +0 -18
- package/dist/qti-test/components/test-section-link.js +0 -9
- package/dist/qti-test/components/test-section-link.js.map +0 -1
- package/dist/qti-test/components/test-show-correct-response.d.ts +0 -39
- package/dist/qti-test/components/test-show-correct-response.js +0 -10
- package/dist/qti-test/components/test-show-correct-response.js.map +0 -1
- package/dist/qti-test/components/test-stamp.d.ts +0 -31
- package/dist/qti-test/components/test-stamp.js +0 -9
- package/dist/qti-test/components/test-stamp.js.map +0 -1
- package/dist/qti-test/components/test-view-toggle.d.ts +0 -19
- package/dist/qti-test/components/test-view-toggle.js +0 -9
- package/dist/qti-test/components/test-view-toggle.js.map +0 -1
- package/dist/qti-test/components/test-view.d.ts +0 -24
- package/dist/qti-test/components/test-view.js +0 -10
- package/dist/qti-test/components/test-view.js.map +0 -1
- package/dist/qti-test/core/index.d.ts +0 -105
- package/dist/qti-test/core/index.js +0 -36
- package/dist/qti-test/core/index.js.map +0 -1
- package/dist/qti-test-feedback-CZsbp6z4.d.ts +0 -91
- package/dist/qti-transform-item-C9WtMeDR.d.ts +0 -39
- /package/dist/qti-item/{core/index.js.map → index.js.map} +0 -0
- /package/dist/qti-test/{components/index.js.map → index.js.map} +0 -0
|
@@ -0,0 +1,2235 @@
|
|
|
1
|
+
import {
|
|
2
|
+
item_default
|
|
3
|
+
} from "./chunk-LSEB52SP.js";
|
|
4
|
+
import {
|
|
5
|
+
INITIAL_TEST_CONTEXT,
|
|
6
|
+
QtiModalFeedback,
|
|
7
|
+
qtiContext,
|
|
8
|
+
testContext
|
|
9
|
+
} from "./chunk-GUDRSXIP.js";
|
|
10
|
+
import {
|
|
11
|
+
configContext,
|
|
12
|
+
watch
|
|
13
|
+
} from "./chunk-O5N4DCU6.js";
|
|
14
|
+
import {
|
|
15
|
+
__decorateClass,
|
|
16
|
+
qtiTransformItem,
|
|
17
|
+
qtiTransformTest
|
|
18
|
+
} from "./chunk-JDY5GL7E.js";
|
|
19
|
+
|
|
20
|
+
// src/lib/qti-test/core/qti-test.ts
|
|
21
|
+
import { html } from "lit";
|
|
22
|
+
import { customElement } from "lit/decorators.js";
|
|
23
|
+
|
|
24
|
+
// src/lib/qti-test/core/mixins/test-navigation.mixin.ts
|
|
25
|
+
import { property } from "lit/decorators.js";
|
|
26
|
+
var NavigationErrorType = /* @__PURE__ */ ((NavigationErrorType2) => {
|
|
27
|
+
NavigationErrorType2["ITEM_NOT_FOUND"] = "item-not-found";
|
|
28
|
+
NavigationErrorType2["SECTION_NOT_FOUND"] = "section-not-found";
|
|
29
|
+
NavigationErrorType2["LOAD_ERROR"] = "load-error";
|
|
30
|
+
NavigationErrorType2["NETWORK_ERROR"] = "network-error";
|
|
31
|
+
NavigationErrorType2["TIMEOUT_ERROR"] = "timeout-error";
|
|
32
|
+
return NavigationErrorType2;
|
|
33
|
+
})(NavigationErrorType || {});
|
|
34
|
+
var TestNavigationMixin = (superClass) => {
|
|
35
|
+
class TestNavigationClass extends superClass {
|
|
36
|
+
constructor(...args) {
|
|
37
|
+
super(...args);
|
|
38
|
+
this.navigate = null;
|
|
39
|
+
this.cacheTransform = false;
|
|
40
|
+
this.requestTimeout = 3e4;
|
|
41
|
+
this.showLoadingIndicators = true;
|
|
42
|
+
this.postLoadTransformCallback = null;
|
|
43
|
+
this._navigationInProgress = false;
|
|
44
|
+
this._activeRequests = [];
|
|
45
|
+
this._lastError = null;
|
|
46
|
+
this._lastNavigationRequestId = null;
|
|
47
|
+
this._targetNavigation = null;
|
|
48
|
+
this.addEventListener(
|
|
49
|
+
"qti-request-navigation",
|
|
50
|
+
async ({ detail }) => {
|
|
51
|
+
if (!detail?.id) return;
|
|
52
|
+
const navigationRequestId = `nav_${Date.now()}_${Math.random()}`;
|
|
53
|
+
this._lastNavigationRequestId = navigationRequestId;
|
|
54
|
+
try {
|
|
55
|
+
this._navigationInProgress = true;
|
|
56
|
+
this._lastError = null;
|
|
57
|
+
this._dispatchStatusEvent({ loading: true, type: detail.type, id: detail.id });
|
|
58
|
+
this._cancelActiveRequests();
|
|
59
|
+
this._targetNavigation = { type: detail.type, id: detail.id };
|
|
60
|
+
if (detail.type === "item") {
|
|
61
|
+
await this._navigateToItem(detail.id);
|
|
62
|
+
} else if (detail.type === "section") {
|
|
63
|
+
await this._navigateToSection(detail.id);
|
|
64
|
+
}
|
|
65
|
+
if (this._lastNavigationRequestId !== navigationRequestId) {
|
|
66
|
+
console.log("Navigation was superseded by a newer request");
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
} catch (error) {
|
|
70
|
+
if (this._lastNavigationRequestId === navigationRequestId) {
|
|
71
|
+
const navError = this._normalizeError(error, detail.type, detail.id);
|
|
72
|
+
this._lastError = navError;
|
|
73
|
+
this._dispatchErrorEvent(navError);
|
|
74
|
+
console.error(`Navigation error (${navError.type}):`, navError.message, navError.details);
|
|
75
|
+
}
|
|
76
|
+
} finally {
|
|
77
|
+
if (this._lastNavigationRequestId === navigationRequestId) {
|
|
78
|
+
this._navigationInProgress = false;
|
|
79
|
+
this._dispatchStatusEvent({ loading: false, type: detail.type, id: detail.id });
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
);
|
|
84
|
+
this.addEventListener("qti-assessment-test-connected", (e) => {
|
|
85
|
+
this._testElement = e.detail;
|
|
86
|
+
this._initializeNavigation();
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Initialize navigation when test is first connected
|
|
91
|
+
*/
|
|
92
|
+
_initializeNavigation() {
|
|
93
|
+
let id;
|
|
94
|
+
if (this.navigate === "section") {
|
|
95
|
+
id = this._testElement.querySelector("qti-assessment-section")?.identifier;
|
|
96
|
+
}
|
|
97
|
+
if (this.navigate === "item") {
|
|
98
|
+
id = this.sessionContext.navItemRefId ?? this._testElement.querySelector("qti-assessment-item-ref")?.identifier;
|
|
99
|
+
}
|
|
100
|
+
if (id) {
|
|
101
|
+
this.dispatchEvent(
|
|
102
|
+
new CustomEvent("qti-request-navigation", {
|
|
103
|
+
detail: { type: this.navigate === "section" ? "section" : "item", id },
|
|
104
|
+
bubbles: true,
|
|
105
|
+
composed: true
|
|
106
|
+
})
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
navigateTo(type, id) {
|
|
111
|
+
if (!id) {
|
|
112
|
+
if (type === "section") {
|
|
113
|
+
id = this._testElement?.querySelector("qti-assessment-section")?.identifier;
|
|
114
|
+
}
|
|
115
|
+
if (type === "item") {
|
|
116
|
+
id = this._testElement?.querySelector("qti-assessment-item-ref")?.identifier;
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
this.dispatchEvent(
|
|
120
|
+
new CustomEvent("qti-request-navigation", {
|
|
121
|
+
detail: { type, id },
|
|
122
|
+
bubbles: true,
|
|
123
|
+
composed: true
|
|
124
|
+
})
|
|
125
|
+
);
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* Navigates to a specific item
|
|
129
|
+
*/
|
|
130
|
+
async _navigateToItem(itemId) {
|
|
131
|
+
const itemRefEl = this._testElement?.querySelector(
|
|
132
|
+
`qti-assessment-item-ref[identifier="${itemId}"]`
|
|
133
|
+
);
|
|
134
|
+
if (!itemRefEl) {
|
|
135
|
+
throw {
|
|
136
|
+
type: "item-not-found" /* ITEM_NOT_FOUND */,
|
|
137
|
+
message: `Item with identifier "${itemId}" not found.`,
|
|
138
|
+
itemId
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
const navPartId = itemRefEl.closest("qti-test-part")?.identifier;
|
|
142
|
+
const navSectionId = itemRefEl.closest("qti-assessment-section")?.identifier;
|
|
143
|
+
this.sessionContext = {
|
|
144
|
+
...this.sessionContext,
|
|
145
|
+
navPartId,
|
|
146
|
+
navSectionId,
|
|
147
|
+
navItemRefId: itemId,
|
|
148
|
+
navItemLoading: true
|
|
149
|
+
};
|
|
150
|
+
try {
|
|
151
|
+
await this._loadItems([itemId]);
|
|
152
|
+
} finally {
|
|
153
|
+
this.sessionContext = {
|
|
154
|
+
...this.sessionContext,
|
|
155
|
+
navItemLoading: false
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
/**
|
|
160
|
+
* Navigates to a specific section
|
|
161
|
+
*/
|
|
162
|
+
async _navigateToSection(sectionId) {
|
|
163
|
+
const sectionRefEl = this._testElement?.querySelector(
|
|
164
|
+
`qti-assessment-section[identifier="${sectionId}"]`
|
|
165
|
+
);
|
|
166
|
+
if (!sectionRefEl) {
|
|
167
|
+
throw {
|
|
168
|
+
type: "section-not-found" /* SECTION_NOT_FOUND */,
|
|
169
|
+
message: `Section with identifier "${sectionId}" not found.`,
|
|
170
|
+
sectionId
|
|
171
|
+
};
|
|
172
|
+
}
|
|
173
|
+
const navPartId = sectionRefEl.closest("qti-test-part")?.identifier;
|
|
174
|
+
this.sessionContext = {
|
|
175
|
+
...this.sessionContext,
|
|
176
|
+
navPartId,
|
|
177
|
+
navSectionId: sectionId,
|
|
178
|
+
navItemRefId: null,
|
|
179
|
+
navItemLoading: true
|
|
180
|
+
};
|
|
181
|
+
try {
|
|
182
|
+
const itemIds = this._getSectionItemIds(sectionId);
|
|
183
|
+
await this._loadItems(itemIds);
|
|
184
|
+
} finally {
|
|
185
|
+
this.sessionContext = {
|
|
186
|
+
...this.sessionContext,
|
|
187
|
+
navItemLoading: false
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
/**
|
|
192
|
+
* Normalize different error types into a consistent NavigationError format
|
|
193
|
+
*/
|
|
194
|
+
_normalizeError(error, navigationType, id) {
|
|
195
|
+
if (error && error.type && Object.values(NavigationErrorType).includes(error.type)) {
|
|
196
|
+
return error;
|
|
197
|
+
}
|
|
198
|
+
if (error instanceof DOMException && error.name === "AbortError") {
|
|
199
|
+
return {
|
|
200
|
+
type: "network-error" /* NETWORK_ERROR */,
|
|
201
|
+
message: "Navigation was cancelled because a new navigation was requested.",
|
|
202
|
+
details: error
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
if (error.name === "TimeoutError" || error.message && error.message.includes("timeout")) {
|
|
206
|
+
return {
|
|
207
|
+
type: "timeout-error" /* TIMEOUT_ERROR */,
|
|
208
|
+
message: "Request timed out. Please check your network connection.",
|
|
209
|
+
details: error
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
if (error instanceof TypeError && error.message.includes("network")) {
|
|
213
|
+
return {
|
|
214
|
+
type: "network-error" /* NETWORK_ERROR */,
|
|
215
|
+
message: "A network error occurred. Please check your connection.",
|
|
216
|
+
details: error
|
|
217
|
+
};
|
|
218
|
+
}
|
|
219
|
+
return {
|
|
220
|
+
type: "load-error" /* LOAD_ERROR */,
|
|
221
|
+
message: `Failed to load ${navigationType}: ${id}`,
|
|
222
|
+
details: error,
|
|
223
|
+
itemId: navigationType === "item" ? id : void 0,
|
|
224
|
+
sectionId: navigationType === "section" ? id : void 0
|
|
225
|
+
};
|
|
226
|
+
}
|
|
227
|
+
/**
|
|
228
|
+
* Dispatch error event to notify the UI
|
|
229
|
+
*/
|
|
230
|
+
_dispatchErrorEvent(error) {
|
|
231
|
+
this.dispatchEvent(
|
|
232
|
+
new CustomEvent("qti-navigation-error", {
|
|
233
|
+
detail: error,
|
|
234
|
+
bubbles: true,
|
|
235
|
+
composed: true
|
|
236
|
+
})
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Dispatch status event to indicate loading state
|
|
241
|
+
*/
|
|
242
|
+
_dispatchStatusEvent(status) {
|
|
243
|
+
if (this.showLoadingIndicators) {
|
|
244
|
+
this.dispatchEvent(
|
|
245
|
+
new CustomEvent("qti-navigation-status", {
|
|
246
|
+
detail: status,
|
|
247
|
+
bubbles: true,
|
|
248
|
+
composed: true
|
|
249
|
+
})
|
|
250
|
+
);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
/**
|
|
254
|
+
* Cancels all active HTTP requests
|
|
255
|
+
*/
|
|
256
|
+
_cancelActiveRequests() {
|
|
257
|
+
if (this._activeRequests.length > 0) {
|
|
258
|
+
console.info(`Cancelling ${this._activeRequests.length} pending requests`);
|
|
259
|
+
this._activeRequests.forEach((request) => {
|
|
260
|
+
if (request && request.readyState !== 4) {
|
|
261
|
+
request.abort();
|
|
262
|
+
}
|
|
263
|
+
});
|
|
264
|
+
this._activeRequests = [];
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
/**
|
|
268
|
+
* Load items with improved error handling and timeout
|
|
269
|
+
*/
|
|
270
|
+
async _loadItems(itemIds) {
|
|
271
|
+
if (!this._testElement || itemIds.length === 0) return;
|
|
272
|
+
const itemRefEls = itemIds.map(
|
|
273
|
+
(id) => this._testElement.querySelector(`qti-assessment-item-ref[identifier="${id}"]`)
|
|
274
|
+
);
|
|
275
|
+
const missingItems = itemRefEls.reduce((missing, el, index) => {
|
|
276
|
+
if (!el) missing.push(itemIds[index]);
|
|
277
|
+
return missing;
|
|
278
|
+
}, []);
|
|
279
|
+
if (missingItems.length > 0) {
|
|
280
|
+
const error = {
|
|
281
|
+
type: "item-not-found" /* ITEM_NOT_FOUND */,
|
|
282
|
+
message: `One or more items not found: ${missingItems.join(", ")}`,
|
|
283
|
+
details: { missingItems }
|
|
284
|
+
};
|
|
285
|
+
throw error;
|
|
286
|
+
}
|
|
287
|
+
this._clearLoadedItems();
|
|
288
|
+
const itemLoadPromises = itemRefEls.map(async (itemRef) => {
|
|
289
|
+
if (!itemRef) return null;
|
|
290
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
291
|
+
setTimeout(() => {
|
|
292
|
+
reject({
|
|
293
|
+
name: "TimeoutError",
|
|
294
|
+
message: `Request for item ${itemRef.identifier} timed out after ${this.requestTimeout}ms`
|
|
295
|
+
});
|
|
296
|
+
}, this.requestTimeout);
|
|
297
|
+
});
|
|
298
|
+
try {
|
|
299
|
+
const { promise, request } = qtiTransformItem(this.cacheTransform).load(itemRef.href);
|
|
300
|
+
if (request instanceof XMLHttpRequest) {
|
|
301
|
+
this._activeRequests.push(request);
|
|
302
|
+
}
|
|
303
|
+
const loadedTransformer = await Promise.race([promise, timeoutPromise]);
|
|
304
|
+
let finalTransformer = loadedTransformer;
|
|
305
|
+
console.log(`Loaded item 123: ${itemRef.identifier}`);
|
|
306
|
+
if (this.postLoadTransformCallback) {
|
|
307
|
+
finalTransformer = await this.postLoadTransformCallback(loadedTransformer, itemRef);
|
|
308
|
+
}
|
|
309
|
+
return {
|
|
310
|
+
itemRef,
|
|
311
|
+
doc: finalTransformer.htmlDoc(),
|
|
312
|
+
request
|
|
313
|
+
};
|
|
314
|
+
} catch (error) {
|
|
315
|
+
if (error instanceof DOMException && error.name === "AbortError" || error && error.name === "TimeoutError") {
|
|
316
|
+
console.log(
|
|
317
|
+
`Request for item ${itemRef.identifier} was ${error.name === "TimeoutError" ? "timed out" : "aborted"}`
|
|
318
|
+
);
|
|
319
|
+
return null;
|
|
320
|
+
}
|
|
321
|
+
error.itemId = itemRef.identifier;
|
|
322
|
+
throw error;
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
try {
|
|
326
|
+
const results = await Promise.all(itemLoadPromises);
|
|
327
|
+
const validResults = results.filter((result) => result !== null);
|
|
328
|
+
validResults.forEach(({ itemRef, doc }) => {
|
|
329
|
+
if (itemRef && doc) itemRef.xmlDoc = doc;
|
|
330
|
+
});
|
|
331
|
+
this._activeRequests = [];
|
|
332
|
+
requestAnimationFrame(() => {
|
|
333
|
+
this.dispatchEvent(
|
|
334
|
+
new CustomEvent("qti-test-loaded", {
|
|
335
|
+
detail: validResults.map(({ itemRef }) => ({
|
|
336
|
+
identifier: itemRef?.identifier,
|
|
337
|
+
element: itemRef
|
|
338
|
+
})),
|
|
339
|
+
bubbles: true,
|
|
340
|
+
composed: true
|
|
341
|
+
})
|
|
342
|
+
);
|
|
343
|
+
});
|
|
344
|
+
if (validResults.length === 0 && itemIds.length > 0) {
|
|
345
|
+
throw {
|
|
346
|
+
type: "load-error" /* LOAD_ERROR */,
|
|
347
|
+
message: "All item requests failed to load",
|
|
348
|
+
details: { itemIds }
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
return validResults;
|
|
352
|
+
} catch (error) {
|
|
353
|
+
console.error("Error loading items:", error);
|
|
354
|
+
throw error;
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
/**
|
|
358
|
+
* Gets all item IDs in a section
|
|
359
|
+
*/
|
|
360
|
+
_getSectionItemIds(navSectionId) {
|
|
361
|
+
const sectionRefEl = this._testElement?.querySelector(
|
|
362
|
+
`qti-assessment-section[identifier="${navSectionId}"]`
|
|
363
|
+
);
|
|
364
|
+
if (!sectionRefEl) {
|
|
365
|
+
throw {
|
|
366
|
+
type: "section-not-found" /* SECTION_NOT_FOUND */,
|
|
367
|
+
message: `Section with identifier "${navSectionId}" not found.`,
|
|
368
|
+
sectionId: navSectionId
|
|
369
|
+
};
|
|
370
|
+
}
|
|
371
|
+
return Array.from(
|
|
372
|
+
this._testElement.querySelectorAll(
|
|
373
|
+
`qti-assessment-section[identifier="${navSectionId}"] > qti-assessment-item-ref`
|
|
374
|
+
)
|
|
375
|
+
).map((itemRef) => itemRef.identifier);
|
|
376
|
+
}
|
|
377
|
+
/**
|
|
378
|
+
* Clears all loaded items
|
|
379
|
+
*/
|
|
380
|
+
_clearLoadedItems() {
|
|
381
|
+
const itemRefEls = this._testElement?.querySelectorAll(
|
|
382
|
+
`qti-assessment-test qti-assessment-item-ref`
|
|
383
|
+
);
|
|
384
|
+
Array.from(itemRefEls || []).forEach((itemElement) => {
|
|
385
|
+
itemElement.xmlDoc = null;
|
|
386
|
+
});
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Retry the last failed navigation
|
|
390
|
+
*/
|
|
391
|
+
retryNavigation() {
|
|
392
|
+
if (this._lastError) {
|
|
393
|
+
const type = this._lastError.itemId ? "item" : "section";
|
|
394
|
+
const id = this._lastError.itemId || this._lastError.sectionId;
|
|
395
|
+
if (id) {
|
|
396
|
+
this.dispatchEvent(
|
|
397
|
+
new CustomEvent("qti-request-navigation", {
|
|
398
|
+
detail: { type, id },
|
|
399
|
+
bubbles: true,
|
|
400
|
+
composed: true
|
|
401
|
+
})
|
|
402
|
+
);
|
|
403
|
+
}
|
|
404
|
+
} else if (this._targetNavigation) {
|
|
405
|
+
this.dispatchEvent(
|
|
406
|
+
new CustomEvent("qti-request-navigation", {
|
|
407
|
+
detail: this._targetNavigation,
|
|
408
|
+
bubbles: true,
|
|
409
|
+
composed: true
|
|
410
|
+
})
|
|
411
|
+
);
|
|
412
|
+
}
|
|
413
|
+
}
|
|
414
|
+
}
|
|
415
|
+
__decorateClass([
|
|
416
|
+
property({ type: String })
|
|
417
|
+
], TestNavigationClass.prototype, "navigate", 2);
|
|
418
|
+
__decorateClass([
|
|
419
|
+
property({ type: Boolean, attribute: "cache-transform" })
|
|
420
|
+
], TestNavigationClass.prototype, "cacheTransform", 2);
|
|
421
|
+
__decorateClass([
|
|
422
|
+
property({ type: Number })
|
|
423
|
+
], TestNavigationClass.prototype, "requestTimeout", 2);
|
|
424
|
+
__decorateClass([
|
|
425
|
+
property({ type: Boolean })
|
|
426
|
+
], TestNavigationClass.prototype, "showLoadingIndicators", 2);
|
|
427
|
+
__decorateClass([
|
|
428
|
+
property({ type: Function })
|
|
429
|
+
], TestNavigationClass.prototype, "postLoadTransformCallback", 2);
|
|
430
|
+
return TestNavigationClass;
|
|
431
|
+
};
|
|
432
|
+
|
|
433
|
+
// src/lib/qti-test/core/mixins/test-view.mixin.ts
|
|
434
|
+
var TestViewMixin = (superClass) => {
|
|
435
|
+
class TestViewClass extends superClass {
|
|
436
|
+
constructor(...args) {
|
|
437
|
+
super(...args);
|
|
438
|
+
this.sessionContext = { ...this.sessionContext, view: "candidate" };
|
|
439
|
+
this.addEventListener("on-test-switch-view", (e) => {
|
|
440
|
+
this.sessionContext = { ...this.sessionContext, view: e.detail };
|
|
441
|
+
this._updateElementView();
|
|
442
|
+
});
|
|
443
|
+
this.addEventListener("qti-assessment-test-connected", () => {
|
|
444
|
+
this._updateElementView();
|
|
445
|
+
});
|
|
446
|
+
this.addEventListener("qti-assessment-item-connected", (e) => {
|
|
447
|
+
this._updateElementView();
|
|
448
|
+
this._setCorrectResponseVisibility(e.detail);
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
willUpdate(changedProperties) {
|
|
452
|
+
super.willUpdate(changedProperties);
|
|
453
|
+
if (changedProperties.has("sessionContext")) {
|
|
454
|
+
this._updateElementView();
|
|
455
|
+
}
|
|
456
|
+
}
|
|
457
|
+
// Method to handle view updates for elements based on the current context view
|
|
458
|
+
_updateElementView() {
|
|
459
|
+
if (this._testElement) {
|
|
460
|
+
const viewElements = Array.from(this._testElement.querySelectorAll("[view]"));
|
|
461
|
+
viewElements.forEach((element) => {
|
|
462
|
+
element.classList.toggle("show", element.getAttribute("view") === this.sessionContext.view);
|
|
463
|
+
});
|
|
464
|
+
const assessmentItemRef = this._testElement.querySelector(
|
|
465
|
+
`qti-assessment-item-ref[identifier="${this.sessionContext.navItemRefId}"]`
|
|
466
|
+
);
|
|
467
|
+
const assessmentItem = assessmentItemRef?.assessmentItem;
|
|
468
|
+
if (assessmentItem) {
|
|
469
|
+
assessmentItem.showCorrectResponse(this.sessionContext.view === "scorer");
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
// Event handler for connected QTI assessment items
|
|
474
|
+
_setCorrectResponseVisibility(assessmentItem) {
|
|
475
|
+
assessmentItem.showCorrectResponse(this.sessionContext.view === "scorer");
|
|
476
|
+
}
|
|
477
|
+
}
|
|
478
|
+
return TestViewClass;
|
|
479
|
+
};
|
|
480
|
+
|
|
481
|
+
// src/lib/qti-test/core/test-base.ts
|
|
482
|
+
import { provide } from "@lit/context";
|
|
483
|
+
import { LitElement } from "lit";
|
|
484
|
+
import { property as property2 } from "lit/decorators.js";
|
|
485
|
+
|
|
486
|
+
// src/lib/exports/session.context.ts
|
|
487
|
+
import { createContext } from "@lit/context";
|
|
488
|
+
var INITIAL_SESSION_CONTEXT = { view: "candidate" };
|
|
489
|
+
var sessionContext = createContext(Symbol("testContext"));
|
|
490
|
+
|
|
491
|
+
// src/lib/qti-test/core/test-base.ts
|
|
492
|
+
var TestBase = class extends LitElement {
|
|
493
|
+
constructor() {
|
|
494
|
+
super();
|
|
495
|
+
this.testContext = INITIAL_TEST_CONTEXT;
|
|
496
|
+
this.sessionContext = INITIAL_SESSION_CONTEXT;
|
|
497
|
+
/**
|
|
498
|
+
* Updates the variables of an assessment item in the test context.
|
|
499
|
+
* - Matches the assessment item with the corresponding test context item.
|
|
500
|
+
* - If the item is not found, logs a warning.
|
|
501
|
+
* - Updates variables in the test context if exactly one variable exists.
|
|
502
|
+
* - Otherwise, syncs the assessment item's variables with the test context.
|
|
503
|
+
*
|
|
504
|
+
* @param assessmentItem - The assessment item to update.
|
|
505
|
+
*/
|
|
506
|
+
this._updateItemInTestContext = (assessmentItem) => {
|
|
507
|
+
const context = assessmentItem._context;
|
|
508
|
+
const identifier = context.identifier;
|
|
509
|
+
const fullVariables = context.variables;
|
|
510
|
+
const itemContext = this.testContext.items.find((i) => i?.identifier === identifier);
|
|
511
|
+
if (!itemContext) {
|
|
512
|
+
console.warn(`Item IDs between assessment.xml and item.xml should match: ${identifier} is not found!`);
|
|
513
|
+
return;
|
|
514
|
+
}
|
|
515
|
+
if (itemContext.variables?.length === 1) {
|
|
516
|
+
this._updateItemVariablesInTestContext(identifier, fullVariables);
|
|
517
|
+
} else {
|
|
518
|
+
assessmentItem.variables = [...itemContext.variables || []];
|
|
519
|
+
}
|
|
520
|
+
};
|
|
521
|
+
this.addEventListener("qti-assessment-test-connected", (e) => {
|
|
522
|
+
this.testContext = INITIAL_TEST_CONTEXT;
|
|
523
|
+
this.sessionContext = INITIAL_SESSION_CONTEXT;
|
|
524
|
+
if (this.testContext && this.testContext.items.length > 0) return;
|
|
525
|
+
this._testElement = e.detail;
|
|
526
|
+
const items = Array.from(this._testElement.querySelectorAll("qti-assessment-item-ref")).map((itemRef) => {
|
|
527
|
+
return {
|
|
528
|
+
href: itemRef.href,
|
|
529
|
+
identifier: itemRef.identifier,
|
|
530
|
+
category: itemRef.category,
|
|
531
|
+
variables: [
|
|
532
|
+
{
|
|
533
|
+
identifier: "completionStatus",
|
|
534
|
+
value: "not_attempted",
|
|
535
|
+
type: "outcome"
|
|
536
|
+
}
|
|
537
|
+
]
|
|
538
|
+
};
|
|
539
|
+
});
|
|
540
|
+
this.testContext = { ...this.testContext, items };
|
|
541
|
+
});
|
|
542
|
+
this.addEventListener("qti-assessment-item-connected", (e) => {
|
|
543
|
+
const assessmentItem = e.detail;
|
|
544
|
+
const assessmentRefId = assessmentItem.closest("qti-assessment-item-ref")?.identifier;
|
|
545
|
+
if (assessmentRefId) {
|
|
546
|
+
assessmentItem.assessmentItemRefId = assessmentRefId;
|
|
547
|
+
}
|
|
548
|
+
this._updateItemInTestContext(e.detail);
|
|
549
|
+
});
|
|
550
|
+
this.addEventListener("qti-item-context-updated", (e) => {
|
|
551
|
+
this._updateItemVariablesInTestContext(e.detail.itemContext.identifier, e.detail.itemContext.variables);
|
|
552
|
+
});
|
|
553
|
+
}
|
|
554
|
+
updateItemVariables(itemRefID, variables) {
|
|
555
|
+
const itemContext = this.testContext.items.find((item) => item.identifier === itemRefID);
|
|
556
|
+
if (itemContext) {
|
|
557
|
+
itemContext.variables = itemContext.variables.map((variable) => {
|
|
558
|
+
const updatedVariable = variables.find((v) => v.identifier === variable.identifier);
|
|
559
|
+
return updatedVariable ? { ...variable, ...updatedVariable } : variable;
|
|
560
|
+
});
|
|
561
|
+
}
|
|
562
|
+
const itemRef = this._testElement.querySelector(
|
|
563
|
+
`qti-assessment-item-ref[identifier="${itemRefID}"]`
|
|
564
|
+
);
|
|
565
|
+
if (itemRef && itemRef.assessmentItem) {
|
|
566
|
+
if (itemRef.assessmentItem) {
|
|
567
|
+
itemRef.assessmentItem.variables = variables;
|
|
568
|
+
}
|
|
569
|
+
}
|
|
570
|
+
}
|
|
571
|
+
_updateItemVariablesInTestContext(identifier, variables) {
|
|
572
|
+
this.testContext = {
|
|
573
|
+
...this.testContext,
|
|
574
|
+
// Spread existing test context properties
|
|
575
|
+
items: this.testContext.items.map((itemContext) => {
|
|
576
|
+
if (itemContext.identifier !== identifier) {
|
|
577
|
+
return itemContext;
|
|
578
|
+
}
|
|
579
|
+
return {
|
|
580
|
+
...itemContext,
|
|
581
|
+
// Keep other properties of the item context
|
|
582
|
+
variables: variables.map((variable) => {
|
|
583
|
+
const matchingVariable = itemContext.variables.find((v) => v.identifier === variable.identifier);
|
|
584
|
+
return matchingVariable ? { ...matchingVariable, ...variable } : variable;
|
|
585
|
+
})
|
|
586
|
+
};
|
|
587
|
+
})
|
|
588
|
+
};
|
|
589
|
+
this.dispatchEvent(
|
|
590
|
+
new CustomEvent("qti-test-context-updated", { detail: this.testContext, bubbles: false, composed: false })
|
|
591
|
+
);
|
|
592
|
+
}
|
|
593
|
+
};
|
|
594
|
+
__decorateClass([
|
|
595
|
+
property2({ attribute: false, type: Object }),
|
|
596
|
+
provide({ context: testContext })
|
|
597
|
+
], TestBase.prototype, "testContext", 2);
|
|
598
|
+
__decorateClass([
|
|
599
|
+
property2({ attribute: false, type: Object }),
|
|
600
|
+
provide({ context: sessionContext })
|
|
601
|
+
], TestBase.prototype, "sessionContext", 2);
|
|
602
|
+
|
|
603
|
+
// src/lib/qti-test/core/mixins/test-processing.mixin.ts
|
|
604
|
+
var TestProcessingMixin = (superClass) => {
|
|
605
|
+
class TestProcessingElement extends superClass {
|
|
606
|
+
constructor(...args) {
|
|
607
|
+
super(...args);
|
|
608
|
+
this.addEventListener("qti-register-variable", (e) => {
|
|
609
|
+
this.testContext = {
|
|
610
|
+
...this.testContext,
|
|
611
|
+
testOutcomeVariables: [...this.testContext.testOutcomeVariables || [], e.detail.variable]
|
|
612
|
+
};
|
|
613
|
+
e.stopPropagation();
|
|
614
|
+
});
|
|
615
|
+
this.addEventListener(
|
|
616
|
+
"qti-set-outcome-value",
|
|
617
|
+
(e) => {
|
|
618
|
+
const { outcomeIdentifier, value } = e.detail;
|
|
619
|
+
this.updateOutcomeVariable(outcomeIdentifier, value);
|
|
620
|
+
e.stopPropagation();
|
|
621
|
+
}
|
|
622
|
+
);
|
|
623
|
+
}
|
|
624
|
+
outcomeProcessing() {
|
|
625
|
+
const outcomeProcessor = this.querySelector("qti-outcome-processing");
|
|
626
|
+
if (!outcomeProcessor) return false;
|
|
627
|
+
outcomeProcessor?.process();
|
|
628
|
+
return true;
|
|
629
|
+
}
|
|
630
|
+
/* --------------------------- ENABLED WHEN UNIT TESTING OUTCOME PROCESSING ------------------------------------ */
|
|
631
|
+
updateOutcomeVariable(identifier, value) {
|
|
632
|
+
const outcomeVariable = this.getOutcome(identifier);
|
|
633
|
+
if (!outcomeVariable) {
|
|
634
|
+
console.warn(`Can not set qti-outcome-identifier: ${identifier}, it is not available`);
|
|
635
|
+
return;
|
|
636
|
+
}
|
|
637
|
+
this.testContext = {
|
|
638
|
+
...this.testContext,
|
|
639
|
+
testOutcomeVariables: this.testContext.testOutcomeVariables?.map((v) => {
|
|
640
|
+
if (v.identifier !== identifier) {
|
|
641
|
+
return v;
|
|
642
|
+
}
|
|
643
|
+
return {
|
|
644
|
+
...v,
|
|
645
|
+
value: outcomeVariable.cardinality === "single" ? value : [...v.value, value]
|
|
646
|
+
};
|
|
647
|
+
})
|
|
648
|
+
};
|
|
649
|
+
}
|
|
650
|
+
getOutcome(identifier) {
|
|
651
|
+
return this.getVariable(identifier);
|
|
652
|
+
}
|
|
653
|
+
getVariable(identifier) {
|
|
654
|
+
return this.testContext.testOutcomeVariables?.find((v) => v.identifier === identifier) || null;
|
|
655
|
+
}
|
|
656
|
+
/* --------------------------- ENABLED WHEN UNIT TESTING OUTCOME PROCESSING ------------------------------------ */
|
|
657
|
+
}
|
|
658
|
+
return TestProcessingElement;
|
|
659
|
+
};
|
|
660
|
+
|
|
661
|
+
// src/lib/qti-test/core/qti-test.ts
|
|
662
|
+
var QtiTest = class extends TestNavigationMixin(TestViewMixin(TestProcessingMixin(TestBase))) {
|
|
663
|
+
// export class QtiTest extends TestLoaderMixin(TestNavigationMixin(TestViewMixin(TestBase))) {
|
|
664
|
+
/**
|
|
665
|
+
* Renders the component's template.
|
|
666
|
+
* Provides a default `<slot>` for content projection.
|
|
667
|
+
*/
|
|
668
|
+
async connectedCallback() {
|
|
669
|
+
super.connectedCallback();
|
|
670
|
+
await this.updateComplete;
|
|
671
|
+
this.dispatchEvent(new CustomEvent("qti-test-connected", { detail: this }));
|
|
672
|
+
}
|
|
673
|
+
render() {
|
|
674
|
+
return html`<slot></slot>`;
|
|
675
|
+
}
|
|
676
|
+
};
|
|
677
|
+
QtiTest = __decorateClass([
|
|
678
|
+
customElement("qti-test")
|
|
679
|
+
], QtiTest);
|
|
680
|
+
|
|
681
|
+
// src/lib/qti-test/core/qti-assessment-test/qti-assessment-item-ref.ts
|
|
682
|
+
import { LitElement as LitElement2 } from "lit";
|
|
683
|
+
import { property as property3 } from "lit/decorators.js";
|
|
684
|
+
import { prepareTemplate } from "@heximal/templates";
|
|
685
|
+
var stringToBooleanConverter = {
|
|
686
|
+
fromAttribute(value) {
|
|
687
|
+
return value === "true";
|
|
688
|
+
},
|
|
689
|
+
toAttribute(value) {
|
|
690
|
+
return value ? "true" : "false";
|
|
691
|
+
}
|
|
692
|
+
};
|
|
693
|
+
var QtiAssessmentItemRef = class extends LitElement2 {
|
|
694
|
+
constructor() {
|
|
695
|
+
super(...arguments);
|
|
696
|
+
// @consume({ context: computedContext, subscribe: true })
|
|
697
|
+
// private computedContext: ComputedContext;
|
|
698
|
+
this.weigths = /* @__PURE__ */ new Map();
|
|
699
|
+
}
|
|
700
|
+
// the XMLDocument
|
|
701
|
+
createRenderRoot() {
|
|
702
|
+
return this;
|
|
703
|
+
}
|
|
704
|
+
get assessmentItem() {
|
|
705
|
+
return this.renderRoot?.querySelector("qti-assessment-item");
|
|
706
|
+
}
|
|
707
|
+
async connectedCallback() {
|
|
708
|
+
super.connectedCallback();
|
|
709
|
+
const templateElement = this.getRootNode().host.closest("qti-test").querySelector("template[item-ref]");
|
|
710
|
+
if (templateElement) this.myTemplate = prepareTemplate(templateElement);
|
|
711
|
+
await this.updateComplete;
|
|
712
|
+
this.dispatchEvent(
|
|
713
|
+
new CustomEvent("qti-assessment-item-ref-connected", {
|
|
714
|
+
bubbles: true,
|
|
715
|
+
composed: true,
|
|
716
|
+
detail: { identifier: this.identifier, href: this.href, category: this.category }
|
|
717
|
+
})
|
|
718
|
+
);
|
|
719
|
+
}
|
|
720
|
+
render() {
|
|
721
|
+
return this.myTemplate ? this.myTemplate({ xmlDoc: this.xmlDoc }) : this.xmlDoc;
|
|
722
|
+
}
|
|
723
|
+
};
|
|
724
|
+
__decorateClass([
|
|
725
|
+
property3({ type: String })
|
|
726
|
+
], QtiAssessmentItemRef.prototype, "category", 2);
|
|
727
|
+
__decorateClass([
|
|
728
|
+
property3({ type: String })
|
|
729
|
+
], QtiAssessmentItemRef.prototype, "identifier", 2);
|
|
730
|
+
__decorateClass([
|
|
731
|
+
property3({ type: Boolean, converter: stringToBooleanConverter })
|
|
732
|
+
], QtiAssessmentItemRef.prototype, "required", 2);
|
|
733
|
+
__decorateClass([
|
|
734
|
+
property3({ type: Boolean, converter: stringToBooleanConverter })
|
|
735
|
+
], QtiAssessmentItemRef.prototype, "fixed", 2);
|
|
736
|
+
__decorateClass([
|
|
737
|
+
property3({ type: String })
|
|
738
|
+
], QtiAssessmentItemRef.prototype, "href", 2);
|
|
739
|
+
__decorateClass([
|
|
740
|
+
property3({ type: Object, attribute: false })
|
|
741
|
+
], QtiAssessmentItemRef.prototype, "xmlDoc", 2);
|
|
742
|
+
if (!customElements.get("qti-assessment-item-ref")) {
|
|
743
|
+
customElements.define("qti-assessment-item-ref", QtiAssessmentItemRef);
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
// src/lib/qti-test/core/qti-assessment-test/qti-assessment-section.ts
|
|
747
|
+
import { consume } from "@lit/context";
|
|
748
|
+
import { html as html2, LitElement as LitElement3 } from "lit";
|
|
749
|
+
import { property as property4 } from "lit/decorators.js";
|
|
750
|
+
var stringToBooleanConverter2 = {
|
|
751
|
+
fromAttribute(value) {
|
|
752
|
+
return value === "true";
|
|
753
|
+
},
|
|
754
|
+
toAttribute(value) {
|
|
755
|
+
return value ? "true" : "false";
|
|
756
|
+
}
|
|
757
|
+
};
|
|
758
|
+
var QtiAssessmentSection = class extends LitElement3 {
|
|
759
|
+
constructor() {
|
|
760
|
+
super(...arguments);
|
|
761
|
+
this._title = "";
|
|
762
|
+
}
|
|
763
|
+
get title() {
|
|
764
|
+
return this._title;
|
|
765
|
+
}
|
|
766
|
+
set title(value) {
|
|
767
|
+
this._title = value;
|
|
768
|
+
this.removeAttribute("title");
|
|
769
|
+
this.setAttribute("data-title", value);
|
|
770
|
+
}
|
|
771
|
+
async connectedCallback() {
|
|
772
|
+
this._title = this.getAttribute("title") || "";
|
|
773
|
+
super.connectedCallback();
|
|
774
|
+
await this.updateComplete;
|
|
775
|
+
this.dispatchEvent(
|
|
776
|
+
new Event("qti-assessment-section-connected", {
|
|
777
|
+
bubbles: true,
|
|
778
|
+
composed: true
|
|
779
|
+
})
|
|
780
|
+
);
|
|
781
|
+
}
|
|
782
|
+
render() {
|
|
783
|
+
return html2`<slot name="qti-rubric-block"></slot><slot></slot>`;
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
__decorateClass([
|
|
787
|
+
property4({ type: String })
|
|
788
|
+
], QtiAssessmentSection.prototype, "identifier", 2);
|
|
789
|
+
__decorateClass([
|
|
790
|
+
property4({ type: String })
|
|
791
|
+
], QtiAssessmentSection.prototype, "required", 2);
|
|
792
|
+
__decorateClass([
|
|
793
|
+
property4({ type: Boolean, converter: stringToBooleanConverter2 })
|
|
794
|
+
], QtiAssessmentSection.prototype, "fixed", 2);
|
|
795
|
+
__decorateClass([
|
|
796
|
+
property4({ type: Boolean, converter: stringToBooleanConverter2 })
|
|
797
|
+
], QtiAssessmentSection.prototype, "visible", 2);
|
|
798
|
+
__decorateClass([
|
|
799
|
+
property4({ type: Boolean, converter: stringToBooleanConverter2, attribute: "keep-together" })
|
|
800
|
+
], QtiAssessmentSection.prototype, "keepTogether", 2);
|
|
801
|
+
__decorateClass([
|
|
802
|
+
consume({ context: testContext, subscribe: true })
|
|
803
|
+
], QtiAssessmentSection.prototype, "_testContext", 2);
|
|
804
|
+
if (!customElements.get("qti-assessment-section")) {
|
|
805
|
+
customElements.define("qti-assessment-section", QtiAssessmentSection);
|
|
806
|
+
}
|
|
807
|
+
|
|
808
|
+
// src/lib/qti-test/core/qti-assessment-test/qti-assessment-test.ts
|
|
809
|
+
import { consume as consume2 } from "@lit/context";
|
|
810
|
+
import { html as html3, LitElement as LitElement4 } from "lit";
|
|
811
|
+
import { customElement as customElement2, property as property5 } from "lit/decorators.js";
|
|
812
|
+
var QtiAssessmentTest = class extends LitElement4 {
|
|
813
|
+
constructor() {
|
|
814
|
+
super(...arguments);
|
|
815
|
+
this._title = "";
|
|
816
|
+
}
|
|
817
|
+
get title() {
|
|
818
|
+
return this._title;
|
|
819
|
+
}
|
|
820
|
+
set title(value) {
|
|
821
|
+
this._title = value;
|
|
822
|
+
this.removeAttribute("title");
|
|
823
|
+
this.setAttribute("data-title", value);
|
|
824
|
+
}
|
|
825
|
+
async connectedCallback() {
|
|
826
|
+
super.connectedCallback();
|
|
827
|
+
await this.updateComplete;
|
|
828
|
+
this.dispatchEvent(
|
|
829
|
+
new CustomEvent("qti-assessment-test-connected", {
|
|
830
|
+
detail: this,
|
|
831
|
+
bubbles: true,
|
|
832
|
+
composed: true
|
|
833
|
+
})
|
|
834
|
+
);
|
|
835
|
+
}
|
|
836
|
+
render() {
|
|
837
|
+
return html3` <slot></slot>`;
|
|
838
|
+
}
|
|
839
|
+
};
|
|
840
|
+
__decorateClass([
|
|
841
|
+
property5({ type: String })
|
|
842
|
+
], QtiAssessmentTest.prototype, "identifier", 2);
|
|
843
|
+
__decorateClass([
|
|
844
|
+
property5({ type: String })
|
|
845
|
+
], QtiAssessmentTest.prototype, "title", 1);
|
|
846
|
+
__decorateClass([
|
|
847
|
+
consume2({ context: testContext, subscribe: true })
|
|
848
|
+
], QtiAssessmentTest.prototype, "_testContext", 2);
|
|
849
|
+
QtiAssessmentTest = __decorateClass([
|
|
850
|
+
customElement2("qti-assessment-test")
|
|
851
|
+
], QtiAssessmentTest);
|
|
852
|
+
|
|
853
|
+
// src/lib/qti-test/core/qti-assessment-test/qti-test-part.ts
|
|
854
|
+
import { html as html4, LitElement as LitElement5 } from "lit";
|
|
855
|
+
import { customElement as customElement3, property as property6 } from "lit/decorators.js";
|
|
856
|
+
var QtiTestPart = class extends LitElement5 {
|
|
857
|
+
constructor() {
|
|
858
|
+
super(...arguments);
|
|
859
|
+
this.identifier = "";
|
|
860
|
+
this.class = "";
|
|
861
|
+
this.navigationMode = "nonlinear";
|
|
862
|
+
this.submissionMode = "individual";
|
|
863
|
+
this._title = "";
|
|
864
|
+
}
|
|
865
|
+
get title() {
|
|
866
|
+
return this._title;
|
|
867
|
+
}
|
|
868
|
+
set title(value) {
|
|
869
|
+
this._title = value;
|
|
870
|
+
this.removeAttribute("title");
|
|
871
|
+
this.setAttribute("data-title", value);
|
|
872
|
+
}
|
|
873
|
+
async connectedCallback() {
|
|
874
|
+
super.connectedCallback();
|
|
875
|
+
await this.updateComplete;
|
|
876
|
+
this.dispatchEvent(
|
|
877
|
+
new Event("qti-test-part-connected", {
|
|
878
|
+
bubbles: true,
|
|
879
|
+
composed: true
|
|
880
|
+
})
|
|
881
|
+
);
|
|
882
|
+
}
|
|
883
|
+
render() {
|
|
884
|
+
return html4` <slot></slot>`;
|
|
885
|
+
}
|
|
886
|
+
};
|
|
887
|
+
__decorateClass([
|
|
888
|
+
property6({ type: String })
|
|
889
|
+
], QtiTestPart.prototype, "identifier", 2);
|
|
890
|
+
__decorateClass([
|
|
891
|
+
property6({ type: String })
|
|
892
|
+
], QtiTestPart.prototype, "class", 2);
|
|
893
|
+
__decorateClass([
|
|
894
|
+
property6({ type: String, attribute: "navigation-mode" })
|
|
895
|
+
], QtiTestPart.prototype, "navigationMode", 2);
|
|
896
|
+
__decorateClass([
|
|
897
|
+
property6({ type: String, attribute: "submission-mode" })
|
|
898
|
+
], QtiTestPart.prototype, "submissionMode", 2);
|
|
899
|
+
QtiTestPart = __decorateClass([
|
|
900
|
+
customElement3("qti-test-part")
|
|
901
|
+
], QtiTestPart);
|
|
902
|
+
if (!customElements.get("qti-test-part")) {
|
|
903
|
+
customElements.define("qti-test-part", QtiTestPart);
|
|
904
|
+
}
|
|
905
|
+
|
|
906
|
+
// src/lib/qti-test/core/qti-assessment-test/qti-test-feedback.ts
|
|
907
|
+
import { customElement as customElement4 } from "lit/decorators.js";
|
|
908
|
+
import { css, html as html5 } from "lit";
|
|
909
|
+
var QtiTestFeedback = class extends QtiModalFeedback {
|
|
910
|
+
render() {
|
|
911
|
+
return html5``;
|
|
912
|
+
}
|
|
913
|
+
};
|
|
914
|
+
QtiTestFeedback.styles = css`
|
|
915
|
+
:host {
|
|
916
|
+
color: gray;
|
|
917
|
+
}
|
|
918
|
+
`;
|
|
919
|
+
QtiTestFeedback = __decorateClass([
|
|
920
|
+
customElement4("qti-test-feedback")
|
|
921
|
+
], QtiTestFeedback);
|
|
922
|
+
|
|
923
|
+
// src/lib/qti-test/components/test-navigation.ts
|
|
924
|
+
import { consume as consume3, provide as provide2 } from "@lit/context";
|
|
925
|
+
import { html as html6, LitElement as LitElement6 } from "lit";
|
|
926
|
+
import { customElement as customElement5, property as property7, state } from "lit/decorators.js";
|
|
927
|
+
|
|
928
|
+
// src/lib/exports/computed.context.ts
|
|
929
|
+
import { createContext as createContext2 } from "@lit/context";
|
|
930
|
+
var computedContext = createContext2(Symbol("computedContext"));
|
|
931
|
+
|
|
932
|
+
// src/lib/qti-test/components/test-navigation.ts
|
|
933
|
+
var TestNavigation = class extends LitElement6 {
|
|
934
|
+
constructor() {
|
|
935
|
+
super();
|
|
936
|
+
this.identifier = void 0;
|
|
937
|
+
this.initContext = [];
|
|
938
|
+
this.qtiContext = {
|
|
939
|
+
QTI_CONTEXT: {
|
|
940
|
+
testIdentifier: "",
|
|
941
|
+
candidateIdentifier: "",
|
|
942
|
+
environmentIdentifier: "default"
|
|
943
|
+
}
|
|
944
|
+
};
|
|
945
|
+
this.configContext = {};
|
|
946
|
+
this.autoScoreItems = false;
|
|
947
|
+
this.addEventListener("qti-assessment-test-connected", this._handleTestConnected.bind(this));
|
|
948
|
+
this.addEventListener("qti-assessment-item-connected", this._handleItemConnected.bind(this));
|
|
949
|
+
this.addEventListener("qti-interaction-changed", this._handleInteractionChanged.bind(this));
|
|
950
|
+
this.addEventListener("test-end-attempt", this._handleTestEndAttempt.bind(this));
|
|
951
|
+
this.addEventListener("test-show-correct-response", this._handleTestShowCorrectResponse.bind(this));
|
|
952
|
+
this.addEventListener("test-show-candidate-correction", this._handleTestShowCandidateCorrection.bind(this));
|
|
953
|
+
this.addEventListener("test-update-outcome-variable", this._handleTestUpdateOutcomeVariable.bind(this));
|
|
954
|
+
}
|
|
955
|
+
/**
|
|
956
|
+
* Handles the 'test-end-attempt' event.
|
|
957
|
+
* @private
|
|
958
|
+
* @listens TestNavigation#test-end-attempt
|
|
959
|
+
* @param {CustomEvent} event - The custom event object.
|
|
960
|
+
*/
|
|
961
|
+
_handleTestEndAttempt(_event) {
|
|
962
|
+
const qtiItemEl = this._testElement.querySelector(
|
|
963
|
+
`qti-assessment-item-ref[identifier="${this._sessionContext.navItemRefId}"]`
|
|
964
|
+
);
|
|
965
|
+
const qtiAssessmentItemEl = qtiItemEl.assessmentItem;
|
|
966
|
+
const reportValidityAfterScoring = this.configContext?.reportValidityAfterScoring === true ? true : false;
|
|
967
|
+
qtiAssessmentItemEl.processResponse(true, reportValidityAfterScoring);
|
|
968
|
+
}
|
|
969
|
+
// protected createRenderRoot(): HTMLElement | DocumentFragment {
|
|
970
|
+
// return this;
|
|
971
|
+
// }
|
|
972
|
+
// myTemplate: TemplateFunction;
|
|
973
|
+
// connectedCallback(): void {
|
|
974
|
+
// super.connectedCallback();
|
|
975
|
+
// const templateElement = this.querySelector<HTMLTemplateElement>('template');
|
|
976
|
+
// if (!templateElement) {
|
|
977
|
+
// this.myTemplate = null;
|
|
978
|
+
// return;
|
|
979
|
+
// }
|
|
980
|
+
// this.myTemplate = prepareTemplate(templateElement);
|
|
981
|
+
// }
|
|
982
|
+
/**
|
|
983
|
+
* Handles the 'test-show-correct-response' event.
|
|
984
|
+
* @private
|
|
985
|
+
* @listens TestNavigation#test-show-correct-response
|
|
986
|
+
* @param {CustomEvent} event - The custom event object.
|
|
987
|
+
*/
|
|
988
|
+
_handleTestShowCorrectResponse(event) {
|
|
989
|
+
const qtiItemEl = this._testElement.querySelector(
|
|
990
|
+
`qti-assessment-item-ref[identifier="${this._sessionContext.navItemRefId}"]`
|
|
991
|
+
);
|
|
992
|
+
const qtiAssessmentItemEl = qtiItemEl.assessmentItem;
|
|
993
|
+
if (!qtiAssessmentItemEl) return;
|
|
994
|
+
qtiAssessmentItemEl.showCorrectResponse(event.detail);
|
|
995
|
+
}
|
|
996
|
+
/**
|
|
997
|
+
* Handles the 'test-show-candidate-correction' event.
|
|
998
|
+
* @private
|
|
999
|
+
* @listens TestNavigation#test-show-candidate-correction
|
|
1000
|
+
* @param {CustomEvent} event - The custom event object.
|
|
1001
|
+
*/
|
|
1002
|
+
_handleTestShowCandidateCorrection(event) {
|
|
1003
|
+
const qtiItemEl = this._testElement.querySelector(
|
|
1004
|
+
`qti-assessment-item-ref[identifier="${this._sessionContext.navItemRefId}"]`
|
|
1005
|
+
);
|
|
1006
|
+
const qtiAssessmentItemEl = qtiItemEl.assessmentItem;
|
|
1007
|
+
qtiAssessmentItemEl.showCandidateCorrection(event.detail);
|
|
1008
|
+
}
|
|
1009
|
+
_handleTestUpdateOutcomeVariable(event) {
|
|
1010
|
+
const qtiItemEl = this._testElement.querySelector(
|
|
1011
|
+
`qti-assessment-item-ref[identifier="${event.detail.assessmentItemRefId}"]`
|
|
1012
|
+
);
|
|
1013
|
+
const qtiAssessmentItemEl = qtiItemEl.assessmentItem;
|
|
1014
|
+
qtiAssessmentItemEl.setOutcomeVariable(event.detail.outcomeVariableId, event.detail.value);
|
|
1015
|
+
}
|
|
1016
|
+
_handleInteractionChanged(_event) {
|
|
1017
|
+
if (this.autoScoreItems) {
|
|
1018
|
+
const assessmentItem = _event.composedPath()[0].closest("qti-assessment-item");
|
|
1019
|
+
const scoreOutcomeIdentifier = assessmentItem.variables.find((v) => v.identifier === "SCORE");
|
|
1020
|
+
if (scoreOutcomeIdentifier && scoreOutcomeIdentifier.externalScored === null && assessmentItem.adaptive === "false") {
|
|
1021
|
+
const reportValidityAfterScoring = this.configContext?.reportValidityAfterScoring === true ? true : false;
|
|
1022
|
+
assessmentItem.processResponse(true, reportValidityAfterScoring);
|
|
1023
|
+
}
|
|
1024
|
+
}
|
|
1025
|
+
}
|
|
1026
|
+
render() {
|
|
1027
|
+
return html6`<slot></slot>`;
|
|
1028
|
+
}
|
|
1029
|
+
/* PK: on test connected we can build the computed context */
|
|
1030
|
+
_handleTestConnected(event) {
|
|
1031
|
+
this._testElement = event.detail;
|
|
1032
|
+
if (!this.qtiContext.QTI_CONTEXT?.testIdentifier) {
|
|
1033
|
+
const currentContext = this.qtiContext.QTI_CONTEXT || {
|
|
1034
|
+
testIdentifier: "",
|
|
1035
|
+
candidateIdentifier: "not set",
|
|
1036
|
+
environmentIdentifier: "default"
|
|
1037
|
+
};
|
|
1038
|
+
this.qtiContext = {
|
|
1039
|
+
QTI_CONTEXT: {
|
|
1040
|
+
...currentContext,
|
|
1041
|
+
testIdentifier: this._testElement.identifier,
|
|
1042
|
+
environmentIdentifier: currentContext.environmentIdentifier || "default"
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
}
|
|
1046
|
+
const contextDeclarations = this._testElement.querySelectorAll('qti-context-declaration[identifier="QTI_CONTEXT"]');
|
|
1047
|
+
contextDeclarations.forEach((declaration) => {
|
|
1048
|
+
const defaultValues = this._extractDefaultValues(declaration);
|
|
1049
|
+
if (Object.keys(defaultValues).length > 0) {
|
|
1050
|
+
this.qtiContext = {
|
|
1051
|
+
QTI_CONTEXT: {
|
|
1052
|
+
...defaultValues,
|
|
1053
|
+
// Default values first
|
|
1054
|
+
...this.qtiContext.QTI_CONTEXT
|
|
1055
|
+
// Runtime values override defaults
|
|
1056
|
+
}
|
|
1057
|
+
};
|
|
1058
|
+
}
|
|
1059
|
+
});
|
|
1060
|
+
const testPartElements = Array.from(this._testElement?.querySelectorAll(`qti-test-part`) || []);
|
|
1061
|
+
this.computedContext = {
|
|
1062
|
+
identifier: this._testElement.identifier,
|
|
1063
|
+
title: this._testElement.title,
|
|
1064
|
+
view: this._sessionContext?.view,
|
|
1065
|
+
testParts: testPartElements.map((testPart) => {
|
|
1066
|
+
const sectionElements = [...testPart.querySelectorAll(`qti-assessment-section`)];
|
|
1067
|
+
return {
|
|
1068
|
+
active: false,
|
|
1069
|
+
identifier: testPart.identifier,
|
|
1070
|
+
navigationMode: testPart.navigationMode,
|
|
1071
|
+
submissionMode: testPart.submissionMode,
|
|
1072
|
+
sections: sectionElements.map((section) => {
|
|
1073
|
+
const itemElements = [...section.querySelectorAll(`qti-assessment-item-ref`)];
|
|
1074
|
+
return {
|
|
1075
|
+
active: false,
|
|
1076
|
+
identifier: section.identifier,
|
|
1077
|
+
title: section.title,
|
|
1078
|
+
items: itemElements.map((item) => ({
|
|
1079
|
+
...this.initContext?.find((i) => i.identifier === item.identifier),
|
|
1080
|
+
active: false,
|
|
1081
|
+
identifier: item.identifier,
|
|
1082
|
+
categories: item.category ? item.category?.split(" ") : [],
|
|
1083
|
+
href: item.href,
|
|
1084
|
+
variables: []
|
|
1085
|
+
}))
|
|
1086
|
+
};
|
|
1087
|
+
})
|
|
1088
|
+
};
|
|
1089
|
+
})
|
|
1090
|
+
};
|
|
1091
|
+
}
|
|
1092
|
+
/**
|
|
1093
|
+
* Extract default values from a qti-context-declaration element
|
|
1094
|
+
*/
|
|
1095
|
+
_extractDefaultValues(declaration) {
|
|
1096
|
+
const defaultValues = {};
|
|
1097
|
+
const defaultValueElement = declaration.querySelector("qti-default-value");
|
|
1098
|
+
if (!defaultValueElement) {
|
|
1099
|
+
return defaultValues;
|
|
1100
|
+
}
|
|
1101
|
+
const valueElements = defaultValueElement.querySelectorAll("qti-value[field-identifier]");
|
|
1102
|
+
valueElements.forEach((valueElement) => {
|
|
1103
|
+
const fieldIdentifier = valueElement.getAttribute("field-identifier");
|
|
1104
|
+
const baseType = valueElement.getAttribute("base-type") || "string";
|
|
1105
|
+
const textContent = valueElement.textContent?.trim() || "";
|
|
1106
|
+
if (fieldIdentifier) {
|
|
1107
|
+
let value = textContent;
|
|
1108
|
+
switch (baseType) {
|
|
1109
|
+
case "integer":
|
|
1110
|
+
value = parseInt(textContent, 10);
|
|
1111
|
+
break;
|
|
1112
|
+
case "float":
|
|
1113
|
+
case "duration":
|
|
1114
|
+
value = parseFloat(textContent);
|
|
1115
|
+
break;
|
|
1116
|
+
case "boolean":
|
|
1117
|
+
value = textContent.toLowerCase() === "true";
|
|
1118
|
+
break;
|
|
1119
|
+
case "string":
|
|
1120
|
+
default:
|
|
1121
|
+
value = textContent;
|
|
1122
|
+
break;
|
|
1123
|
+
}
|
|
1124
|
+
defaultValues[fieldIdentifier] = value;
|
|
1125
|
+
}
|
|
1126
|
+
});
|
|
1127
|
+
return defaultValues;
|
|
1128
|
+
}
|
|
1129
|
+
/* PK: on item connected we can add item only properties in the xml */
|
|
1130
|
+
_handleItemConnected(event) {
|
|
1131
|
+
const itemElement = event.detail;
|
|
1132
|
+
this.computedContext = {
|
|
1133
|
+
...this.computedContext,
|
|
1134
|
+
testParts: this.computedContext.testParts.map((testPart) => {
|
|
1135
|
+
return {
|
|
1136
|
+
...testPart,
|
|
1137
|
+
sections: testPart.sections.map((section) => {
|
|
1138
|
+
return {
|
|
1139
|
+
...section,
|
|
1140
|
+
items: section.items.map((item) => {
|
|
1141
|
+
if (item.identifier !== itemElement.parentElement.getAttribute("identifier")) {
|
|
1142
|
+
return item;
|
|
1143
|
+
}
|
|
1144
|
+
const scoreOutcome = itemElement.querySelector(
|
|
1145
|
+
"qti-outcome-declaration[identifier='SCORE']"
|
|
1146
|
+
);
|
|
1147
|
+
const externalScored = scoreOutcome?.getAttribute("externalScored");
|
|
1148
|
+
const responseDeclarations = itemElement.querySelectorAll("qti-response-declaration");
|
|
1149
|
+
const containsCorrectResponse = Array.from(responseDeclarations).some(
|
|
1150
|
+
(r) => r.querySelector("qti-correct-response")
|
|
1151
|
+
);
|
|
1152
|
+
const containsMapping = Array.from(responseDeclarations).some((r) => {
|
|
1153
|
+
const mapping = r.querySelector("qti-mapping");
|
|
1154
|
+
const areaMapping = r.querySelector("qti-area-mapping");
|
|
1155
|
+
return mapping?.querySelector("qti-map-entry") || areaMapping?.querySelector("qti-area-map-entry");
|
|
1156
|
+
});
|
|
1157
|
+
const hasCorrectResponse = containsCorrectResponse || containsMapping;
|
|
1158
|
+
const hasResponseProcessing = itemElement.querySelector("qti-response-processing") ? true : false;
|
|
1159
|
+
return {
|
|
1160
|
+
...item,
|
|
1161
|
+
assessmentItemIdentifier: itemElement.getAttribute("identifier"),
|
|
1162
|
+
label: itemElement.getAttribute("label"),
|
|
1163
|
+
title: itemElement.title,
|
|
1164
|
+
externalScored,
|
|
1165
|
+
adaptive: itemElement.adaptive == "true" || false,
|
|
1166
|
+
timeDependent: itemElement.timeDependent == "true" || false,
|
|
1167
|
+
variables: itemElement.variables,
|
|
1168
|
+
hasCorrectResponse,
|
|
1169
|
+
hasResponseProcessing
|
|
1170
|
+
};
|
|
1171
|
+
})
|
|
1172
|
+
};
|
|
1173
|
+
})
|
|
1174
|
+
};
|
|
1175
|
+
})
|
|
1176
|
+
};
|
|
1177
|
+
}
|
|
1178
|
+
/* PK: on every change of the candidate we will recomputed the computedContext */
|
|
1179
|
+
willUpdate(_changedProperties) {
|
|
1180
|
+
if (!this.computedContext) return;
|
|
1181
|
+
let itemIndex = 1;
|
|
1182
|
+
this.computedContext = {
|
|
1183
|
+
...this.computedContext,
|
|
1184
|
+
view: this._sessionContext?.view,
|
|
1185
|
+
testParts: this.computedContext.testParts.map((testPart) => {
|
|
1186
|
+
return {
|
|
1187
|
+
...testPart,
|
|
1188
|
+
active: this._sessionContext?.navPartId === testPart.identifier || false,
|
|
1189
|
+
sections: testPart.sections.map((section) => {
|
|
1190
|
+
return {
|
|
1191
|
+
...section,
|
|
1192
|
+
active: this._sessionContext?.navSectionId === section.identifier || false,
|
|
1193
|
+
completed: section.items.every(
|
|
1194
|
+
(item) => this._testContext.items.find((i) => i.identifier === item.identifier)?.variables.find((v) => v.identifier === "completionStatus").value === "completed"
|
|
1195
|
+
),
|
|
1196
|
+
items: section.items.map((item) => {
|
|
1197
|
+
const itemContext = this._testContext?.items.find((i) => i.identifier === item.identifier);
|
|
1198
|
+
const computedItem = {
|
|
1199
|
+
...item,
|
|
1200
|
+
...itemContext,
|
|
1201
|
+
...this.initContext?.find((i) => i.identifier === item.identifier)
|
|
1202
|
+
};
|
|
1203
|
+
const rawscore = computedItem.variables?.find((vr) => vr.identifier == "SCORE")?.value;
|
|
1204
|
+
const score = rawscore === void 0 || rawscore === null ? null : parseFloat(rawscore?.toString());
|
|
1205
|
+
const completionStatus = computedItem.variables?.find((v) => v.identifier === "completionStatus")?.value;
|
|
1206
|
+
const response = computedItem.variables?.find((v) => v.identifier === "RESPONSE")?.value || "";
|
|
1207
|
+
const numAttempts = computedItem.variables?.find((v) => v.identifier === "numAttempts")?.value || 0;
|
|
1208
|
+
const active = this._sessionContext?.navItemRefId === computedItem.identifier || false;
|
|
1209
|
+
const index = item.categories.includes(this.configContext?.infoItemCategory) ? null : itemIndex++;
|
|
1210
|
+
const rawMaxScore = item.variables?.find((vr) => vr.identifier == "MAXSCORE")?.value;
|
|
1211
|
+
const maxScore = rawMaxScore === void 0 || rawMaxScore === null ? null : parseFloat(rawMaxScore?.toString());
|
|
1212
|
+
return {
|
|
1213
|
+
...computedItem,
|
|
1214
|
+
completionStatus,
|
|
1215
|
+
numAttempts,
|
|
1216
|
+
score,
|
|
1217
|
+
response,
|
|
1218
|
+
index,
|
|
1219
|
+
// type,
|
|
1220
|
+
active,
|
|
1221
|
+
// correct,
|
|
1222
|
+
maxScore
|
|
1223
|
+
// incorrect,
|
|
1224
|
+
// completed
|
|
1225
|
+
};
|
|
1226
|
+
})
|
|
1227
|
+
};
|
|
1228
|
+
})
|
|
1229
|
+
};
|
|
1230
|
+
})
|
|
1231
|
+
};
|
|
1232
|
+
this.dispatchEvent(
|
|
1233
|
+
new CustomEvent("qti-computed-context-updated", {
|
|
1234
|
+
detail: this.computedContext,
|
|
1235
|
+
bubbles: true
|
|
1236
|
+
})
|
|
1237
|
+
);
|
|
1238
|
+
}
|
|
1239
|
+
};
|
|
1240
|
+
__decorateClass([
|
|
1241
|
+
property7({ type: String })
|
|
1242
|
+
], TestNavigation.prototype, "identifier", 2);
|
|
1243
|
+
__decorateClass([
|
|
1244
|
+
state()
|
|
1245
|
+
], TestNavigation.prototype, "initContext", 2);
|
|
1246
|
+
__decorateClass([
|
|
1247
|
+
state(),
|
|
1248
|
+
provide2({ context: qtiContext })
|
|
1249
|
+
], TestNavigation.prototype, "qtiContext", 2);
|
|
1250
|
+
__decorateClass([
|
|
1251
|
+
state(),
|
|
1252
|
+
provide2({ context: configContext })
|
|
1253
|
+
], TestNavigation.prototype, "configContext", 2);
|
|
1254
|
+
__decorateClass([
|
|
1255
|
+
state(),
|
|
1256
|
+
consume3({ context: testContext, subscribe: true })
|
|
1257
|
+
], TestNavigation.prototype, "_testContext", 2);
|
|
1258
|
+
__decorateClass([
|
|
1259
|
+
state(),
|
|
1260
|
+
consume3({ context: sessionContext, subscribe: true })
|
|
1261
|
+
], TestNavigation.prototype, "_sessionContext", 2);
|
|
1262
|
+
__decorateClass([
|
|
1263
|
+
state(),
|
|
1264
|
+
provide2({ context: computedContext })
|
|
1265
|
+
], TestNavigation.prototype, "computedContext", 2);
|
|
1266
|
+
__decorateClass([
|
|
1267
|
+
property7({ type: Boolean, attribute: "auto-score-items" })
|
|
1268
|
+
], TestNavigation.prototype, "autoScoreItems", 2);
|
|
1269
|
+
TestNavigation = __decorateClass([
|
|
1270
|
+
customElement5("test-navigation")
|
|
1271
|
+
], TestNavigation);
|
|
1272
|
+
|
|
1273
|
+
// src/lib/qti-test/components/test-next.ts
|
|
1274
|
+
import { css as css3, html as html7, LitElement as LitElement7 } from "lit";
|
|
1275
|
+
import { customElement as customElement6, property as property8 } from "lit/decorators.js";
|
|
1276
|
+
import { consume as consume4 } from "@lit/context";
|
|
1277
|
+
|
|
1278
|
+
// src/lib/qti-test/components/styles.ts
|
|
1279
|
+
import { css as css2 } from "lit";
|
|
1280
|
+
var form = css2`
|
|
1281
|
+
display: inline-flex;
|
|
1282
|
+
align-items: center;
|
|
1283
|
+
cursor: pointer;
|
|
1284
|
+
padding: 0.5rem 1rem;
|
|
1285
|
+
border-radius: 0.25rem;
|
|
1286
|
+
user-select: none;
|
|
1287
|
+
`;
|
|
1288
|
+
var btn = css2`
|
|
1289
|
+
background-color: lightgray;
|
|
1290
|
+
${form};
|
|
1291
|
+
`;
|
|
1292
|
+
var dis = css2`
|
|
1293
|
+
cursor: not-allowed;
|
|
1294
|
+
opacity: 0.8;
|
|
1295
|
+
`;
|
|
1296
|
+
var ind = css2`
|
|
1297
|
+
${form};
|
|
1298
|
+
border: 1px solid gray;
|
|
1299
|
+
`;
|
|
1300
|
+
|
|
1301
|
+
// src/lib/qti-test/components/test-next.ts
|
|
1302
|
+
var TestNext = class extends LitElement7 {
|
|
1303
|
+
constructor() {
|
|
1304
|
+
super();
|
|
1305
|
+
this._internalDisabled = true;
|
|
1306
|
+
this._internals = this.attachInternals();
|
|
1307
|
+
this._internals.role = "button";
|
|
1308
|
+
this._internals.ariaLabel = "Next item";
|
|
1309
|
+
this.addEventListener("click", (e) => {
|
|
1310
|
+
e.preventDefault();
|
|
1311
|
+
if (!this._internalDisabled) this._requestItem(this.sectionItems[this.itemIndex + 1].identifier);
|
|
1312
|
+
});
|
|
1313
|
+
}
|
|
1314
|
+
_handleTestElementChange(_oldValue, newValue) {
|
|
1315
|
+
if (newValue) {
|
|
1316
|
+
this._internalDisabled = false;
|
|
1317
|
+
}
|
|
1318
|
+
}
|
|
1319
|
+
connectedCallback() {
|
|
1320
|
+
super.connectedCallback();
|
|
1321
|
+
this.checkDisabled();
|
|
1322
|
+
}
|
|
1323
|
+
willUpdate(_changedProperties) {
|
|
1324
|
+
if (!this.computedContext) return;
|
|
1325
|
+
const testPart = this.computedContext?.testParts.find((testPart2) => testPart2.active);
|
|
1326
|
+
if (!testPart) return;
|
|
1327
|
+
this.sectionItems = testPart.sections.flatMap((section) => section.items);
|
|
1328
|
+
this.itemIndex = this.sectionItems.findIndex((item) => item.active);
|
|
1329
|
+
this.checkDisabled();
|
|
1330
|
+
}
|
|
1331
|
+
checkDisabled() {
|
|
1332
|
+
this._internalDisabled = !this.computedContext || this.itemIndex < 0 || this.itemIndex >= this.sectionItems?.length - 1;
|
|
1333
|
+
}
|
|
1334
|
+
_requestItem(identifier) {
|
|
1335
|
+
this.dispatchEvent(
|
|
1336
|
+
new CustomEvent("qti-request-navigation", {
|
|
1337
|
+
composed: true,
|
|
1338
|
+
bubbles: true,
|
|
1339
|
+
detail: {
|
|
1340
|
+
type: "item",
|
|
1341
|
+
id: identifier
|
|
1342
|
+
}
|
|
1343
|
+
})
|
|
1344
|
+
);
|
|
1345
|
+
}
|
|
1346
|
+
render() {
|
|
1347
|
+
return html7`<slot></slot>`;
|
|
1348
|
+
}
|
|
1349
|
+
};
|
|
1350
|
+
TestNext.styles = css3`
|
|
1351
|
+
:host {
|
|
1352
|
+
${btn};
|
|
1353
|
+
}
|
|
1354
|
+
:host([disabled]) {
|
|
1355
|
+
${dis};
|
|
1356
|
+
}
|
|
1357
|
+
`;
|
|
1358
|
+
__decorateClass([
|
|
1359
|
+
property8({ type: Boolean, reflect: true, attribute: "disabled" })
|
|
1360
|
+
], TestNext.prototype, "_internalDisabled", 2);
|
|
1361
|
+
__decorateClass([
|
|
1362
|
+
consume4({ context: computedContext, subscribe: true })
|
|
1363
|
+
], TestNext.prototype, "computedContext", 2);
|
|
1364
|
+
__decorateClass([
|
|
1365
|
+
watch("computedContext")
|
|
1366
|
+
], TestNext.prototype, "_handleTestElementChange", 1);
|
|
1367
|
+
TestNext = __decorateClass([
|
|
1368
|
+
customElement6("test-next")
|
|
1369
|
+
], TestNext);
|
|
1370
|
+
|
|
1371
|
+
// src/lib/qti-test/components/test-prev.ts
|
|
1372
|
+
import { css as css4, html as html8, LitElement as LitElement8 } from "lit";
|
|
1373
|
+
import { customElement as customElement7, property as property9 } from "lit/decorators.js";
|
|
1374
|
+
import { consume as consume5 } from "@lit/context";
|
|
1375
|
+
var TestPrev = class extends LitElement8 {
|
|
1376
|
+
constructor() {
|
|
1377
|
+
super();
|
|
1378
|
+
this._internalDisabled = true;
|
|
1379
|
+
this.addEventListener("click", (e) => {
|
|
1380
|
+
e.preventDefault();
|
|
1381
|
+
if (!this._internalDisabled) this._requestItem(this.sectionItems[this.itemIndex - 1].identifier);
|
|
1382
|
+
});
|
|
1383
|
+
}
|
|
1384
|
+
_handleTestElementChange(_oldValue, newValue) {
|
|
1385
|
+
if (newValue) {
|
|
1386
|
+
this._internalDisabled = false;
|
|
1387
|
+
}
|
|
1388
|
+
}
|
|
1389
|
+
willUpdate(_changedProperties) {
|
|
1390
|
+
if (!this.computedContext) return;
|
|
1391
|
+
const testPart = this.computedContext?.testParts.find((testPart2) => testPart2.active);
|
|
1392
|
+
if (!testPart) return;
|
|
1393
|
+
this.sectionItems = testPart.sections.flatMap((section) => section.items);
|
|
1394
|
+
this.itemIndex = this.sectionItems.findIndex((item) => item.active);
|
|
1395
|
+
this.checkDisabled();
|
|
1396
|
+
}
|
|
1397
|
+
checkDisabled() {
|
|
1398
|
+
this._internalDisabled = !this.computedContext || this.itemIndex === 0 || this.itemIndex === -1;
|
|
1399
|
+
}
|
|
1400
|
+
_requestItem(identifier) {
|
|
1401
|
+
this.dispatchEvent(
|
|
1402
|
+
new CustomEvent("qti-request-navigation", {
|
|
1403
|
+
composed: true,
|
|
1404
|
+
bubbles: true,
|
|
1405
|
+
detail: {
|
|
1406
|
+
type: "item",
|
|
1407
|
+
id: identifier
|
|
1408
|
+
}
|
|
1409
|
+
})
|
|
1410
|
+
);
|
|
1411
|
+
}
|
|
1412
|
+
render() {
|
|
1413
|
+
return html8`<slot></slot>`;
|
|
1414
|
+
}
|
|
1415
|
+
};
|
|
1416
|
+
TestPrev.styles = css4`
|
|
1417
|
+
:host {
|
|
1418
|
+
${btn};
|
|
1419
|
+
}
|
|
1420
|
+
:host([disabled]) {
|
|
1421
|
+
${dis};
|
|
1422
|
+
}
|
|
1423
|
+
`;
|
|
1424
|
+
__decorateClass([
|
|
1425
|
+
property9({ type: Boolean, reflect: true, attribute: "disabled" })
|
|
1426
|
+
], TestPrev.prototype, "_internalDisabled", 2);
|
|
1427
|
+
__decorateClass([
|
|
1428
|
+
consume5({ context: computedContext, subscribe: true })
|
|
1429
|
+
], TestPrev.prototype, "computedContext", 2);
|
|
1430
|
+
__decorateClass([
|
|
1431
|
+
watch("computedContext")
|
|
1432
|
+
], TestPrev.prototype, "_handleTestElementChange", 1);
|
|
1433
|
+
TestPrev = __decorateClass([
|
|
1434
|
+
customElement7("test-prev")
|
|
1435
|
+
], TestPrev);
|
|
1436
|
+
|
|
1437
|
+
// src/lib/qti-test/components/test-view.ts
|
|
1438
|
+
import { html as html9, LitElement as LitElement9 } from "lit";
|
|
1439
|
+
import { customElement as customElement8, property as property10, state as state2 } from "lit/decorators.js";
|
|
1440
|
+
import { consume as consume6 } from "@lit/context";
|
|
1441
|
+
var TestView = class extends LitElement9 {
|
|
1442
|
+
constructor() {
|
|
1443
|
+
super(...arguments);
|
|
1444
|
+
this.label = "view";
|
|
1445
|
+
this._handleViewOptionsChange = () => {
|
|
1446
|
+
this.updateViewOptions();
|
|
1447
|
+
};
|
|
1448
|
+
this._viewOptions = TestView.DEFAULT_VIEW_OPTIONS;
|
|
1449
|
+
}
|
|
1450
|
+
connectedCallback() {
|
|
1451
|
+
super.connectedCallback();
|
|
1452
|
+
this.updateViewOptions();
|
|
1453
|
+
}
|
|
1454
|
+
updateViewOptions() {
|
|
1455
|
+
if (this.viewOptions) {
|
|
1456
|
+
const options = this.viewOptions.split(",").map((opt) => opt.trim());
|
|
1457
|
+
this._viewOptions = options.filter((opt) => TestView.DEFAULT_VIEW_OPTIONS.includes(opt));
|
|
1458
|
+
} else {
|
|
1459
|
+
this._viewOptions = TestView.DEFAULT_VIEW_OPTIONS;
|
|
1460
|
+
}
|
|
1461
|
+
}
|
|
1462
|
+
_switchView(view) {
|
|
1463
|
+
this.dispatchEvent(
|
|
1464
|
+
new CustomEvent("on-test-switch-view", {
|
|
1465
|
+
composed: true,
|
|
1466
|
+
bubbles: true,
|
|
1467
|
+
detail: view
|
|
1468
|
+
})
|
|
1469
|
+
);
|
|
1470
|
+
}
|
|
1471
|
+
render() {
|
|
1472
|
+
return html9`
|
|
1473
|
+
<label part="label" for="viewSelect">${this.label}</label>
|
|
1474
|
+
<select
|
|
1475
|
+
part="select"
|
|
1476
|
+
id="viewSelect"
|
|
1477
|
+
@change=${(e) => {
|
|
1478
|
+
const el = e.target;
|
|
1479
|
+
this._switchView(el.value);
|
|
1480
|
+
}}
|
|
1481
|
+
>
|
|
1482
|
+
${this._viewOptions.map(
|
|
1483
|
+
(v) => html9`<option value="${v}" ?selected=${v === this.sessionContext.view}>${v}</option>`
|
|
1484
|
+
)}
|
|
1485
|
+
</select>
|
|
1486
|
+
`;
|
|
1487
|
+
}
|
|
1488
|
+
};
|
|
1489
|
+
TestView.DEFAULT_VIEW_OPTIONS = ["author", "candidate", "proctor", "scorer", "testConstructor", "tutor"];
|
|
1490
|
+
__decorateClass([
|
|
1491
|
+
consume6({ context: sessionContext, subscribe: true })
|
|
1492
|
+
], TestView.prototype, "sessionContext", 2);
|
|
1493
|
+
__decorateClass([
|
|
1494
|
+
property10({ type: String })
|
|
1495
|
+
], TestView.prototype, "label", 2);
|
|
1496
|
+
__decorateClass([
|
|
1497
|
+
property10({ type: String, attribute: "view-options" })
|
|
1498
|
+
], TestView.prototype, "viewOptions", 2);
|
|
1499
|
+
__decorateClass([
|
|
1500
|
+
watch("viewOptions", { waitUntilFirstUpdate: true })
|
|
1501
|
+
], TestView.prototype, "_handleViewOptionsChange", 2);
|
|
1502
|
+
__decorateClass([
|
|
1503
|
+
state2()
|
|
1504
|
+
], TestView.prototype, "_viewOptions", 2);
|
|
1505
|
+
TestView = __decorateClass([
|
|
1506
|
+
customElement8("test-view")
|
|
1507
|
+
], TestView);
|
|
1508
|
+
|
|
1509
|
+
// src/lib/qti-test/components/test-item-link.ts
|
|
1510
|
+
import { css as css5, html as html10, LitElement as LitElement10 } from "lit";
|
|
1511
|
+
import { customElement as customElement9, property as property11 } from "lit/decorators.js";
|
|
1512
|
+
var TestItemLink = class extends LitElement10 {
|
|
1513
|
+
constructor() {
|
|
1514
|
+
super();
|
|
1515
|
+
this.itemId = null;
|
|
1516
|
+
this.addEventListener("click", () => this._requestItem(this.itemId));
|
|
1517
|
+
}
|
|
1518
|
+
_requestItem(identifier) {
|
|
1519
|
+
this.dispatchEvent(
|
|
1520
|
+
new CustomEvent("qti-request-navigation", {
|
|
1521
|
+
composed: true,
|
|
1522
|
+
bubbles: true,
|
|
1523
|
+
detail: {
|
|
1524
|
+
type: "item",
|
|
1525
|
+
id: identifier
|
|
1526
|
+
}
|
|
1527
|
+
})
|
|
1528
|
+
);
|
|
1529
|
+
}
|
|
1530
|
+
render() {
|
|
1531
|
+
return html10` <slot></slot> `;
|
|
1532
|
+
}
|
|
1533
|
+
};
|
|
1534
|
+
TestItemLink.styles = css5`
|
|
1535
|
+
:host {
|
|
1536
|
+
${btn};
|
|
1537
|
+
}
|
|
1538
|
+
:host([disabled]) {
|
|
1539
|
+
${dis};
|
|
1540
|
+
}
|
|
1541
|
+
`;
|
|
1542
|
+
__decorateClass([
|
|
1543
|
+
property11({ type: String, attribute: "item-id" })
|
|
1544
|
+
], TestItemLink.prototype, "itemId", 2);
|
|
1545
|
+
TestItemLink = __decorateClass([
|
|
1546
|
+
customElement9("test-item-link")
|
|
1547
|
+
], TestItemLink);
|
|
1548
|
+
|
|
1549
|
+
// src/lib/qti-test/components/test-end-attempt.ts
|
|
1550
|
+
import { css as css6, html as html11, LitElement as LitElement11 } from "lit";
|
|
1551
|
+
import { customElement as customElement10 } from "lit/decorators.js";
|
|
1552
|
+
var TestEndAttempt = class extends LitElement11 {
|
|
1553
|
+
constructor() {
|
|
1554
|
+
super();
|
|
1555
|
+
this.addEventListener("click", () => this.dispatchEvent(new CustomEvent("test-end-attempt", { bubbles: true })));
|
|
1556
|
+
}
|
|
1557
|
+
render() {
|
|
1558
|
+
return html11` <slot></slot> `;
|
|
1559
|
+
}
|
|
1560
|
+
};
|
|
1561
|
+
TestEndAttempt.styles = css6`
|
|
1562
|
+
:host {
|
|
1563
|
+
${btn};
|
|
1564
|
+
}
|
|
1565
|
+
:host([disabled]) {
|
|
1566
|
+
${dis};
|
|
1567
|
+
}
|
|
1568
|
+
`;
|
|
1569
|
+
TestEndAttempt = __decorateClass([
|
|
1570
|
+
customElement10("test-end-attempt")
|
|
1571
|
+
], TestEndAttempt);
|
|
1572
|
+
|
|
1573
|
+
// src/lib/qti-test/components/test-show-correct-response.ts
|
|
1574
|
+
import { css as css7, html as html12, LitElement as LitElement12 } from "lit";
|
|
1575
|
+
import { customElement as customElement11, property as property12 } from "lit/decorators.js";
|
|
1576
|
+
import { consume as consume7 } from "@lit/context";
|
|
1577
|
+
var TestShowCorrectResponse = class extends LitElement12 {
|
|
1578
|
+
constructor() {
|
|
1579
|
+
super(...arguments);
|
|
1580
|
+
this.shown = false;
|
|
1581
|
+
this.disabled = false;
|
|
1582
|
+
this.showCorrectText = "Show correct response";
|
|
1583
|
+
this.hideCorrectText = "Hide correct response";
|
|
1584
|
+
this.noCorrectResponseText = "No correct response specified";
|
|
1585
|
+
}
|
|
1586
|
+
// Store previous active item reference
|
|
1587
|
+
willUpdate(_changedProperties) {
|
|
1588
|
+
const activeItem = this.computedContext?.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items)).find((item) => item.active);
|
|
1589
|
+
if (this._previousActiveItem !== activeItem) {
|
|
1590
|
+
this.shown = false;
|
|
1591
|
+
this._previousActiveItem = activeItem;
|
|
1592
|
+
}
|
|
1593
|
+
if (activeItem) {
|
|
1594
|
+
const containsCorrectResponse = !!activeItem?.variables?.some((v) => v["correctResponse"]);
|
|
1595
|
+
const containsMapping = !!activeItem?.variables?.some((v) => {
|
|
1596
|
+
return v["mapping"]?.mapEntries?.length > 0 || v["areaMapping"]?.areaMapEntries?.length > 0;
|
|
1597
|
+
});
|
|
1598
|
+
this.disabled = !containsCorrectResponse && !containsMapping;
|
|
1599
|
+
} else {
|
|
1600
|
+
this.disabled = true;
|
|
1601
|
+
}
|
|
1602
|
+
}
|
|
1603
|
+
_toggleState() {
|
|
1604
|
+
if (this.disabled) return;
|
|
1605
|
+
this.shown = !this.shown;
|
|
1606
|
+
this.dispatchEvent(
|
|
1607
|
+
new CustomEvent("test-show-correct-response", {
|
|
1608
|
+
detail: this.shown,
|
|
1609
|
+
bubbles: true
|
|
1610
|
+
})
|
|
1611
|
+
);
|
|
1612
|
+
}
|
|
1613
|
+
_getDisplayedText() {
|
|
1614
|
+
return this.disabled ? this.noCorrectResponseText : this.shown ? this.hideCorrectText : this.showCorrectText;
|
|
1615
|
+
}
|
|
1616
|
+
render() {
|
|
1617
|
+
return html12` <div @click="${this._toggleState}">${this._getDisplayedText()}</div> `;
|
|
1618
|
+
}
|
|
1619
|
+
};
|
|
1620
|
+
TestShowCorrectResponse.styles = css7`
|
|
1621
|
+
:host {
|
|
1622
|
+
${btn};
|
|
1623
|
+
}
|
|
1624
|
+
:host([disabled]) {
|
|
1625
|
+
${dis};
|
|
1626
|
+
}
|
|
1627
|
+
`;
|
|
1628
|
+
__decorateClass([
|
|
1629
|
+
consume7({ context: computedContext, subscribe: true })
|
|
1630
|
+
], TestShowCorrectResponse.prototype, "computedContext", 2);
|
|
1631
|
+
__decorateClass([
|
|
1632
|
+
property12({ type: Boolean, reflect: true })
|
|
1633
|
+
], TestShowCorrectResponse.prototype, "shown", 2);
|
|
1634
|
+
__decorateClass([
|
|
1635
|
+
property12({ type: Boolean, reflect: true })
|
|
1636
|
+
], TestShowCorrectResponse.prototype, "disabled", 2);
|
|
1637
|
+
__decorateClass([
|
|
1638
|
+
property12({ type: String })
|
|
1639
|
+
], TestShowCorrectResponse.prototype, "showCorrectText", 2);
|
|
1640
|
+
__decorateClass([
|
|
1641
|
+
property12({ type: String })
|
|
1642
|
+
], TestShowCorrectResponse.prototype, "hideCorrectText", 2);
|
|
1643
|
+
__decorateClass([
|
|
1644
|
+
property12({ type: String })
|
|
1645
|
+
], TestShowCorrectResponse.prototype, "noCorrectResponseText", 2);
|
|
1646
|
+
TestShowCorrectResponse = __decorateClass([
|
|
1647
|
+
customElement11("test-show-correct-response")
|
|
1648
|
+
], TestShowCorrectResponse);
|
|
1649
|
+
|
|
1650
|
+
// src/lib/qti-test/components/test-paging-buttons-stamp.ts
|
|
1651
|
+
import { html as html13, LitElement as LitElement13 } from "lit";
|
|
1652
|
+
import { customElement as customElement12 } from "lit/decorators.js";
|
|
1653
|
+
import { prepareTemplate as prepareTemplate2 } from "@heximal/templates";
|
|
1654
|
+
import { consume as consume8 } from "@lit/context";
|
|
1655
|
+
var TestPagingButtonsStamp = class extends LitElement13 {
|
|
1656
|
+
createRenderRoot() {
|
|
1657
|
+
return this;
|
|
1658
|
+
}
|
|
1659
|
+
constructor() {
|
|
1660
|
+
super();
|
|
1661
|
+
this._internals = this.attachInternals();
|
|
1662
|
+
this._internals.ariaLabel = "pagination";
|
|
1663
|
+
}
|
|
1664
|
+
connectedCallback() {
|
|
1665
|
+
super.connectedCallback();
|
|
1666
|
+
const templateElement = this.querySelector("template");
|
|
1667
|
+
this.myTemplate = prepareTemplate2(templateElement);
|
|
1668
|
+
}
|
|
1669
|
+
render() {
|
|
1670
|
+
if (!this.computedContext) return html13``;
|
|
1671
|
+
const items = this.computedContext.testParts.flatMap(
|
|
1672
|
+
(testPart) => testPart.sections.flatMap((section) => section.items)
|
|
1673
|
+
);
|
|
1674
|
+
return html13` ${items.map((item) => this.myTemplate({ item, view: this.computedContext.view }))} `;
|
|
1675
|
+
}
|
|
1676
|
+
};
|
|
1677
|
+
__decorateClass([
|
|
1678
|
+
consume8({ context: computedContext, subscribe: true })
|
|
1679
|
+
], TestPagingButtonsStamp.prototype, "computedContext", 2);
|
|
1680
|
+
TestPagingButtonsStamp = __decorateClass([
|
|
1681
|
+
customElement12("test-paging-buttons-stamp")
|
|
1682
|
+
], TestPagingButtonsStamp);
|
|
1683
|
+
|
|
1684
|
+
// src/lib/qti-test/components/test-container.ts
|
|
1685
|
+
import { LitElement as LitElement14, html as html14 } from "lit";
|
|
1686
|
+
import { customElement as customElement13, property as property13, state as state3 } from "lit/decorators.js";
|
|
1687
|
+
import { until } from "lit/directives/until.js";
|
|
1688
|
+
var TestContainer = class extends LitElement14 {
|
|
1689
|
+
constructor() {
|
|
1690
|
+
super(...arguments);
|
|
1691
|
+
this.testURL = null;
|
|
1692
|
+
this.testDoc = null;
|
|
1693
|
+
this.testXML = null;
|
|
1694
|
+
/** Template content if provided */
|
|
1695
|
+
this.templateContent = null;
|
|
1696
|
+
}
|
|
1697
|
+
async handleTestURLChange() {
|
|
1698
|
+
if (!this.testURL) return;
|
|
1699
|
+
try {
|
|
1700
|
+
const api = await qtiTransformTest().load(this.testURL);
|
|
1701
|
+
this.testDoc = api.htmlDoc();
|
|
1702
|
+
} catch (error) {
|
|
1703
|
+
console.error("Error loading or parsing XML:", error);
|
|
1704
|
+
}
|
|
1705
|
+
}
|
|
1706
|
+
handleTestXMLChange() {
|
|
1707
|
+
if (!this.testXML) return;
|
|
1708
|
+
try {
|
|
1709
|
+
this.testDoc = qtiTransformTest().parse(this.testXML).htmlDoc();
|
|
1710
|
+
} catch (error) {
|
|
1711
|
+
console.error("Error parsing XML:", error);
|
|
1712
|
+
}
|
|
1713
|
+
}
|
|
1714
|
+
async connectedCallback() {
|
|
1715
|
+
super.connectedCallback();
|
|
1716
|
+
this.initializeTemplateContent();
|
|
1717
|
+
this.applyStyles();
|
|
1718
|
+
if (this.testURL) {
|
|
1719
|
+
this.handleTestURLChange();
|
|
1720
|
+
}
|
|
1721
|
+
if (this.testXML) {
|
|
1722
|
+
this.handleTestXMLChange();
|
|
1723
|
+
}
|
|
1724
|
+
}
|
|
1725
|
+
initializeTemplateContent() {
|
|
1726
|
+
const template = this.querySelector("template");
|
|
1727
|
+
this.templateContent = template ? template.content : html14``;
|
|
1728
|
+
}
|
|
1729
|
+
applyStyles() {
|
|
1730
|
+
const sheet = new CSSStyleSheet();
|
|
1731
|
+
sheet.replaceSync(item_default);
|
|
1732
|
+
this.shadowRoot.adoptedStyleSheets = [sheet];
|
|
1733
|
+
}
|
|
1734
|
+
render() {
|
|
1735
|
+
return html14`
|
|
1736
|
+
${this.templateContent}
|
|
1737
|
+
<slot></slot>
|
|
1738
|
+
${until(this.testDoc, html14`<span>Loading...</span>`)}
|
|
1739
|
+
`;
|
|
1740
|
+
}
|
|
1741
|
+
};
|
|
1742
|
+
__decorateClass([
|
|
1743
|
+
property13({ type: String, attribute: "test-url" })
|
|
1744
|
+
], TestContainer.prototype, "testURL", 2);
|
|
1745
|
+
__decorateClass([
|
|
1746
|
+
state3()
|
|
1747
|
+
], TestContainer.prototype, "testDoc", 2);
|
|
1748
|
+
__decorateClass([
|
|
1749
|
+
state3()
|
|
1750
|
+
], TestContainer.prototype, "testXML", 2);
|
|
1751
|
+
__decorateClass([
|
|
1752
|
+
watch("testURL", { waitUntilFirstUpdate: true })
|
|
1753
|
+
], TestContainer.prototype, "handleTestURLChange", 1);
|
|
1754
|
+
__decorateClass([
|
|
1755
|
+
watch("testXML", { waitUntilFirstUpdate: true })
|
|
1756
|
+
], TestContainer.prototype, "handleTestXMLChange", 1);
|
|
1757
|
+
TestContainer = __decorateClass([
|
|
1758
|
+
customElement13("test-container")
|
|
1759
|
+
], TestContainer);
|
|
1760
|
+
|
|
1761
|
+
// src/lib/qti-test/components/test-print-item-variables.ts
|
|
1762
|
+
import { html as html15, css as css8, LitElement as LitElement15 } from "lit";
|
|
1763
|
+
import { consume as consume9 } from "@lit/context";
|
|
1764
|
+
import { customElement as customElement14 } from "lit/decorators.js";
|
|
1765
|
+
var TestPrintVariables = class extends LitElement15 {
|
|
1766
|
+
render() {
|
|
1767
|
+
const activeItem = this.computedContext?.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items)).find((item) => item.active);
|
|
1768
|
+
if (!activeItem || !activeItem.variables) return html15``;
|
|
1769
|
+
const responseVariables = activeItem.variables.filter((v) => v.type === "response");
|
|
1770
|
+
const outcomeVariables = activeItem.variables.filter((v) => v.type === "outcome");
|
|
1771
|
+
const renderTable = (variables, title) => html15`
|
|
1772
|
+
<h3>${title}</h3>
|
|
1773
|
+
<table>
|
|
1774
|
+
<thead>
|
|
1775
|
+
<tr>
|
|
1776
|
+
<th>Identifier</th>
|
|
1777
|
+
<th>Value</th>
|
|
1778
|
+
<th>Cardinality</th>
|
|
1779
|
+
<th>Base Type</th>
|
|
1780
|
+
<th>Correct Response / Mappings</th>
|
|
1781
|
+
</tr>
|
|
1782
|
+
</thead>
|
|
1783
|
+
<tbody>
|
|
1784
|
+
${variables.map((v) => {
|
|
1785
|
+
const correctResponse = v.correctResponse ? Array.isArray(v.correctResponse) ? v.correctResponse.join(", ") : v.correctResponse : "";
|
|
1786
|
+
const mapEntries = v.mapping?.mapEntries?.map((m) => `${m.mapKey}=${m.mappedValue}pt`).join(", ") || "";
|
|
1787
|
+
const areaMapEntries = v.areaMapping?.areaMapEntries?.map((m) => `${m.shape}(${m.coords})=${m.mappedValue}pt`).join(", ") || "";
|
|
1788
|
+
return html15`
|
|
1789
|
+
<tr>
|
|
1790
|
+
<td>${v.identifier}</td>
|
|
1791
|
+
<td>${Array.isArray(v.value) ? v.value.join(", ") : v.value}</td>
|
|
1792
|
+
<td>${v.cardinality}</td>
|
|
1793
|
+
<td>${v.baseType}</td>
|
|
1794
|
+
<td>${correctResponse || mapEntries || areaMapEntries}</td>
|
|
1795
|
+
</tr>
|
|
1796
|
+
`;
|
|
1797
|
+
})}
|
|
1798
|
+
</tbody>
|
|
1799
|
+
</table>
|
|
1800
|
+
`;
|
|
1801
|
+
return html15`
|
|
1802
|
+
${renderTable(responseVariables, "Response Variables")} ${renderTable(outcomeVariables, "Outcome Variables")}
|
|
1803
|
+
`;
|
|
1804
|
+
}
|
|
1805
|
+
};
|
|
1806
|
+
TestPrintVariables.styles = css8`
|
|
1807
|
+
table {
|
|
1808
|
+
width: 100%;
|
|
1809
|
+
border-collapse: collapse;
|
|
1810
|
+
margin: 20px 0;
|
|
1811
|
+
font-size: 14px;
|
|
1812
|
+
text-align: left;
|
|
1813
|
+
}
|
|
1814
|
+
th,
|
|
1815
|
+
td {
|
|
1816
|
+
border: 1px solid #ddd;
|
|
1817
|
+
padding: 8px;
|
|
1818
|
+
}
|
|
1819
|
+
th {
|
|
1820
|
+
background-color: #f4f4f4;
|
|
1821
|
+
font-weight: bold;
|
|
1822
|
+
}
|
|
1823
|
+
h3 {
|
|
1824
|
+
margin-top: 20px;
|
|
1825
|
+
font-size: 16px;
|
|
1826
|
+
}
|
|
1827
|
+
`;
|
|
1828
|
+
__decorateClass([
|
|
1829
|
+
consume9({ context: computedContext, subscribe: true })
|
|
1830
|
+
], TestPrintVariables.prototype, "computedContext", 2);
|
|
1831
|
+
TestPrintVariables = __decorateClass([
|
|
1832
|
+
customElement14("test-print-item-variables")
|
|
1833
|
+
], TestPrintVariables);
|
|
1834
|
+
|
|
1835
|
+
// src/lib/qti-test/components/test-section-buttons-stamp.ts
|
|
1836
|
+
import { html as html16, LitElement as LitElement16 } from "lit";
|
|
1837
|
+
import { customElement as customElement15 } from "lit/decorators.js";
|
|
1838
|
+
import { prepareTemplate as prepareTemplate3 } from "@heximal/templates";
|
|
1839
|
+
import { consume as consume10 } from "@lit/context";
|
|
1840
|
+
var TestSectionButtonsStamp = class extends LitElement16 {
|
|
1841
|
+
createRenderRoot() {
|
|
1842
|
+
return this;
|
|
1843
|
+
}
|
|
1844
|
+
constructor() {
|
|
1845
|
+
super();
|
|
1846
|
+
this._internals = this.attachInternals();
|
|
1847
|
+
this._internals.ariaLabel = "pagination";
|
|
1848
|
+
}
|
|
1849
|
+
connectedCallback() {
|
|
1850
|
+
super.connectedCallback();
|
|
1851
|
+
const templateElement = this.querySelector("template");
|
|
1852
|
+
this.myTemplate = prepareTemplate3(templateElement);
|
|
1853
|
+
}
|
|
1854
|
+
render() {
|
|
1855
|
+
if (!this.computedContext) return html16``;
|
|
1856
|
+
const sections = this.computedContext.testParts.flatMap((testPart) => testPart.sections);
|
|
1857
|
+
return html16` ${sections.map((item) => this.myTemplate({ item }))} `;
|
|
1858
|
+
}
|
|
1859
|
+
};
|
|
1860
|
+
__decorateClass([
|
|
1861
|
+
consume10({ context: computedContext, subscribe: true })
|
|
1862
|
+
], TestSectionButtonsStamp.prototype, "computedContext", 2);
|
|
1863
|
+
TestSectionButtonsStamp = __decorateClass([
|
|
1864
|
+
customElement15("test-section-buttons-stamp")
|
|
1865
|
+
], TestSectionButtonsStamp);
|
|
1866
|
+
|
|
1867
|
+
// src/lib/qti-test/components/test-section-link.ts
|
|
1868
|
+
import { css as css9, html as html17, LitElement as LitElement17 } from "lit";
|
|
1869
|
+
import { customElement as customElement16, property as property14 } from "lit/decorators.js";
|
|
1870
|
+
var TestSectionLink = class extends LitElement17 {
|
|
1871
|
+
constructor() {
|
|
1872
|
+
super();
|
|
1873
|
+
this.sectionId = null;
|
|
1874
|
+
this.addEventListener("click", () => this._requestItem(this.sectionId));
|
|
1875
|
+
}
|
|
1876
|
+
_requestItem(identifier) {
|
|
1877
|
+
this.dispatchEvent(
|
|
1878
|
+
new CustomEvent("qti-request-navigation", {
|
|
1879
|
+
composed: true,
|
|
1880
|
+
bubbles: true,
|
|
1881
|
+
detail: {
|
|
1882
|
+
type: "section",
|
|
1883
|
+
id: identifier
|
|
1884
|
+
}
|
|
1885
|
+
})
|
|
1886
|
+
);
|
|
1887
|
+
}
|
|
1888
|
+
render() {
|
|
1889
|
+
return html17` <slot></slot> `;
|
|
1890
|
+
}
|
|
1891
|
+
};
|
|
1892
|
+
TestSectionLink.styles = css9`
|
|
1893
|
+
:host {
|
|
1894
|
+
${btn};
|
|
1895
|
+
}
|
|
1896
|
+
:host([disabled]) {
|
|
1897
|
+
${dis};
|
|
1898
|
+
}
|
|
1899
|
+
`;
|
|
1900
|
+
__decorateClass([
|
|
1901
|
+
property14({ type: String, attribute: "section-id" })
|
|
1902
|
+
], TestSectionLink.prototype, "sectionId", 2);
|
|
1903
|
+
TestSectionLink = __decorateClass([
|
|
1904
|
+
customElement16("test-section-link")
|
|
1905
|
+
], TestSectionLink);
|
|
1906
|
+
|
|
1907
|
+
// src/lib/qti-test/components/test-print-context.ts
|
|
1908
|
+
import { html as html18, LitElement as LitElement18 } from "lit";
|
|
1909
|
+
import { consume as consume11 } from "@lit/context";
|
|
1910
|
+
import { customElement as customElement17, state as state4 } from "lit/decorators.js";
|
|
1911
|
+
var TestPrintContext = class extends LitElement18 {
|
|
1912
|
+
render() {
|
|
1913
|
+
return html18` <small><pre>${JSON.stringify(this.computedContext, null, 2)}</pre></small> `;
|
|
1914
|
+
}
|
|
1915
|
+
};
|
|
1916
|
+
__decorateClass([
|
|
1917
|
+
state4(),
|
|
1918
|
+
consume11({ context: computedContext, subscribe: true })
|
|
1919
|
+
], TestPrintContext.prototype, "computedContext", 2);
|
|
1920
|
+
TestPrintContext = __decorateClass([
|
|
1921
|
+
customElement17("test-print-context")
|
|
1922
|
+
], TestPrintContext);
|
|
1923
|
+
|
|
1924
|
+
// src/lib/qti-test/components/test-stamp.ts
|
|
1925
|
+
import { html as html19, LitElement as LitElement19, nothing } from "lit";
|
|
1926
|
+
import { customElement as customElement18, property as property15, state as state5 } from "lit/decorators.js";
|
|
1927
|
+
import { prepareTemplate as prepareTemplate4 } from "@heximal/templates";
|
|
1928
|
+
import { consume as consume12 } from "@lit/context";
|
|
1929
|
+
var TestStamp = class extends LitElement19 {
|
|
1930
|
+
constructor() {
|
|
1931
|
+
super(...arguments);
|
|
1932
|
+
this.debug = false;
|
|
1933
|
+
this.stampContext = {
|
|
1934
|
+
view: "candidate",
|
|
1935
|
+
activeItem: {},
|
|
1936
|
+
activeSection: {
|
|
1937
|
+
items: []
|
|
1938
|
+
},
|
|
1939
|
+
activeTestpart: {
|
|
1940
|
+
items: [],
|
|
1941
|
+
sections: []
|
|
1942
|
+
},
|
|
1943
|
+
test: {}
|
|
1944
|
+
};
|
|
1945
|
+
}
|
|
1946
|
+
createRenderRoot() {
|
|
1947
|
+
return this;
|
|
1948
|
+
}
|
|
1949
|
+
connectedCallback() {
|
|
1950
|
+
super.connectedCallback();
|
|
1951
|
+
const templateElement = this.querySelector("template");
|
|
1952
|
+
if (!templateElement) {
|
|
1953
|
+
this.myTemplate = null;
|
|
1954
|
+
return;
|
|
1955
|
+
}
|
|
1956
|
+
this.myTemplate = prepareTemplate4(templateElement);
|
|
1957
|
+
}
|
|
1958
|
+
willUpdate(_changedProperties) {
|
|
1959
|
+
if (!this.computedContext) {
|
|
1960
|
+
return;
|
|
1961
|
+
}
|
|
1962
|
+
const activeTestPart = this.computedContext.testParts.find((testPart) => testPart.active);
|
|
1963
|
+
const activeSection = activeTestPart?.sections.find((section) => section.active);
|
|
1964
|
+
const activeItem = activeSection?.items.find((item) => item.active);
|
|
1965
|
+
const { variables, ...augmentedItem } = activeItem || {};
|
|
1966
|
+
if (!activeTestPart || !activeSection || !activeItem) {
|
|
1967
|
+
return;
|
|
1968
|
+
}
|
|
1969
|
+
const augmentedTestPart = {
|
|
1970
|
+
...activeTestPart,
|
|
1971
|
+
items: activeTestPart.sections.flatMap((section) => section.items.map(({ variables: variables2, ...rest }) => rest)),
|
|
1972
|
+
sections: activeTestPart.sections.map((section) => ({
|
|
1973
|
+
...section,
|
|
1974
|
+
items: section.items.map(({ variables: variables2, ...rest }) => rest)
|
|
1975
|
+
}))
|
|
1976
|
+
};
|
|
1977
|
+
const augmentedSection = { ...activeSection, items: activeSection.items.map(({ variables: variables2, ...rest }) => rest) };
|
|
1978
|
+
const { testParts, ...activeTest } = this.computedContext;
|
|
1979
|
+
this.stampContext = {
|
|
1980
|
+
view: this.computedContext.view,
|
|
1981
|
+
activeItem: augmentedItem,
|
|
1982
|
+
activeSection: augmentedSection,
|
|
1983
|
+
activeTestpart: augmentedTestPart,
|
|
1984
|
+
test: activeTest
|
|
1985
|
+
};
|
|
1986
|
+
}
|
|
1987
|
+
render() {
|
|
1988
|
+
return html19` ${this.debug ? html19`<small><pre>${JSON.stringify(this.stampContext, null, 2)}</pre></small>` : nothing}
|
|
1989
|
+
${this.myTemplate ? this.myTemplate(this.stampContext) : nothing}`;
|
|
1990
|
+
}
|
|
1991
|
+
};
|
|
1992
|
+
__decorateClass([
|
|
1993
|
+
property15({ type: Boolean, reflect: true })
|
|
1994
|
+
], TestStamp.prototype, "debug", 2);
|
|
1995
|
+
__decorateClass([
|
|
1996
|
+
state5(),
|
|
1997
|
+
consume12({ context: computedContext, subscribe: true })
|
|
1998
|
+
], TestStamp.prototype, "computedContext", 2);
|
|
1999
|
+
__decorateClass([
|
|
2000
|
+
state5()
|
|
2001
|
+
], TestStamp.prototype, "stampContext", 2);
|
|
2002
|
+
TestStamp = __decorateClass([
|
|
2003
|
+
customElement18("test-stamp")
|
|
2004
|
+
], TestStamp);
|
|
2005
|
+
|
|
2006
|
+
// src/lib/qti-test/components/test-scoring-buttons.ts
|
|
2007
|
+
import { html as html20, LitElement as LitElement20, nothing as nothing2 } from "lit";
|
|
2008
|
+
import { consume as consume13 } from "@lit/context";
|
|
2009
|
+
import { customElement as customElement19, property as property16 } from "lit/decorators.js";
|
|
2010
|
+
import { prepareTemplate as prepareTemplate5 } from "@heximal/templates";
|
|
2011
|
+
var TestScoringButtons = class extends LitElement20 {
|
|
2012
|
+
constructor() {
|
|
2013
|
+
super();
|
|
2014
|
+
this.view = "";
|
|
2015
|
+
this.disabled = false;
|
|
2016
|
+
this.myTemplate = null;
|
|
2017
|
+
this.addEventListener("click", (e) => {
|
|
2018
|
+
const target = e.target;
|
|
2019
|
+
const value = parseFloat(target.value);
|
|
2020
|
+
if (target.tagName === "INPUT") {
|
|
2021
|
+
this._changeOutcomeScore(value);
|
|
2022
|
+
}
|
|
2023
|
+
});
|
|
2024
|
+
}
|
|
2025
|
+
createRenderRoot() {
|
|
2026
|
+
return this;
|
|
2027
|
+
}
|
|
2028
|
+
connectedCallback() {
|
|
2029
|
+
super.connectedCallback();
|
|
2030
|
+
const templateElement = this.querySelector("template");
|
|
2031
|
+
if (!templateElement) {
|
|
2032
|
+
this.myTemplate = null;
|
|
2033
|
+
return;
|
|
2034
|
+
}
|
|
2035
|
+
this.myTemplate = prepareTemplate5(templateElement);
|
|
2036
|
+
}
|
|
2037
|
+
_changeOutcomeScore(value) {
|
|
2038
|
+
const testPart = this.computedContext?.testParts.find((testPart2) => testPart2.active);
|
|
2039
|
+
const sectionItems = testPart.sections.flatMap((section) => section.items);
|
|
2040
|
+
const currentItemIdentifier = sectionItems.find((item) => item.active)?.identifier;
|
|
2041
|
+
this.dispatchEvent(
|
|
2042
|
+
new CustomEvent("test-update-outcome-variable", {
|
|
2043
|
+
detail: {
|
|
2044
|
+
assessmentItemRefId: currentItemIdentifier,
|
|
2045
|
+
outcomeVariableId: "SCORE",
|
|
2046
|
+
value
|
|
2047
|
+
},
|
|
2048
|
+
bubbles: true
|
|
2049
|
+
})
|
|
2050
|
+
);
|
|
2051
|
+
}
|
|
2052
|
+
render() {
|
|
2053
|
+
const activeItem = this.computedContext?.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items)).find((item) => item.active);
|
|
2054
|
+
if (!activeItem || !activeItem.variables) return html20``;
|
|
2055
|
+
const maxScore = activeItem.variables.find((vr) => vr.identifier == "MAXSCORE")?.value;
|
|
2056
|
+
const scoreOutcome = activeItem.variables.find((vr) => vr.identifier == "SCORE");
|
|
2057
|
+
const score = scoreOutcome?.value;
|
|
2058
|
+
const disabled = !(scoreOutcome?.externalScored === "human");
|
|
2059
|
+
if (!maxScore || !scoreOutcome) return nothing2;
|
|
2060
|
+
const scores = [...Array(Number(maxScore) + 1).keys()];
|
|
2061
|
+
return html20`${this.myTemplate ? this.myTemplate({ scores, score, disabled }) : nothing2}`;
|
|
2062
|
+
}
|
|
2063
|
+
};
|
|
2064
|
+
__decorateClass([
|
|
2065
|
+
property16({ type: String, attribute: "view" })
|
|
2066
|
+
], TestScoringButtons.prototype, "view", 2);
|
|
2067
|
+
__decorateClass([
|
|
2068
|
+
property16({ type: Boolean })
|
|
2069
|
+
], TestScoringButtons.prototype, "disabled", 2);
|
|
2070
|
+
__decorateClass([
|
|
2071
|
+
consume13({ context: computedContext, subscribe: true })
|
|
2072
|
+
], TestScoringButtons.prototype, "computedContext", 2);
|
|
2073
|
+
TestScoringButtons = __decorateClass([
|
|
2074
|
+
customElement19("test-scoring-buttons")
|
|
2075
|
+
], TestScoringButtons);
|
|
2076
|
+
|
|
2077
|
+
// src/lib/qti-test/components/test-view-toggle.ts
|
|
2078
|
+
import { consume as consume14 } from "@lit/context";
|
|
2079
|
+
import { html as html21, LitElement as LitElement21, nothing as nothing3 } from "lit";
|
|
2080
|
+
import { customElement as customElement20 } from "lit/decorators.js";
|
|
2081
|
+
import { prepareTemplate as prepareTemplate6 } from "@heximal/templates";
|
|
2082
|
+
var TestViewToggle = class extends LitElement21 {
|
|
2083
|
+
createRenderRoot() {
|
|
2084
|
+
return this;
|
|
2085
|
+
}
|
|
2086
|
+
connectedCallback() {
|
|
2087
|
+
super.connectedCallback();
|
|
2088
|
+
const templateElement = this.querySelector("template");
|
|
2089
|
+
if (!templateElement) {
|
|
2090
|
+
this.myTemplate = null;
|
|
2091
|
+
return;
|
|
2092
|
+
}
|
|
2093
|
+
this.myTemplate = prepareTemplate6(templateElement);
|
|
2094
|
+
}
|
|
2095
|
+
_switchView(view) {
|
|
2096
|
+
this.dispatchEvent(
|
|
2097
|
+
new CustomEvent("on-test-switch-view", {
|
|
2098
|
+
composed: true,
|
|
2099
|
+
bubbles: true,
|
|
2100
|
+
detail: view
|
|
2101
|
+
})
|
|
2102
|
+
);
|
|
2103
|
+
}
|
|
2104
|
+
firstUpdated(_changedProperties) {
|
|
2105
|
+
this.addEventListener("click", () => {
|
|
2106
|
+
if (this.sessionContext?.view === "scorer") {
|
|
2107
|
+
this._switchView("candidate");
|
|
2108
|
+
} else {
|
|
2109
|
+
this._switchView("scorer");
|
|
2110
|
+
}
|
|
2111
|
+
});
|
|
2112
|
+
}
|
|
2113
|
+
render() {
|
|
2114
|
+
return html21`${this.myTemplate ? this.myTemplate({
|
|
2115
|
+
view: this.sessionContext?.view
|
|
2116
|
+
}) : nothing3}`;
|
|
2117
|
+
}
|
|
2118
|
+
};
|
|
2119
|
+
__decorateClass([
|
|
2120
|
+
consume14({ context: sessionContext, subscribe: true })
|
|
2121
|
+
], TestViewToggle.prototype, "sessionContext", 2);
|
|
2122
|
+
TestViewToggle = __decorateClass([
|
|
2123
|
+
customElement20("test-view-toggle")
|
|
2124
|
+
], TestViewToggle);
|
|
2125
|
+
|
|
2126
|
+
// src/lib/qti-test/components/test-scoring-feedback.ts
|
|
2127
|
+
import { consume as consume15 } from "@lit/context";
|
|
2128
|
+
import { html as html22, LitElement as LitElement22 } from "lit";
|
|
2129
|
+
import { customElement as customElement21, property as property17 } from "lit/decorators.js";
|
|
2130
|
+
var TestScoringFeedback = class extends LitElement22 {
|
|
2131
|
+
constructor() {
|
|
2132
|
+
super(...arguments);
|
|
2133
|
+
this.view = null;
|
|
2134
|
+
}
|
|
2135
|
+
render() {
|
|
2136
|
+
const activeItem = this.computedContext?.testParts.flatMap((testPart) => testPart.sections.flatMap((section) => section.items)).find((item) => item.active);
|
|
2137
|
+
if (!activeItem || !activeItem.variables) return html22``;
|
|
2138
|
+
if (activeItem["category"] === "dep-informational") return html22`<div>${this.dataset.informational}</div>`;
|
|
2139
|
+
const completionStatus = activeItem?.variables.find((v) => v.identifier === "completionStatus")?.value;
|
|
2140
|
+
const scoreOutcome = activeItem?.variables.find((vr) => vr.identifier == "SCORE");
|
|
2141
|
+
const score = parseFloat(scoreOutcome?.value);
|
|
2142
|
+
const externalScored = activeItem["externalScored"];
|
|
2143
|
+
const feedbackText = () => {
|
|
2144
|
+
if (completionStatus !== "completed") {
|
|
2145
|
+
return this.dataset.textNoResponse;
|
|
2146
|
+
}
|
|
2147
|
+
if (!externalScored && score !== void 0 && !Number.isNaN(score)) {
|
|
2148
|
+
return score > 0 ? this.dataset.textCorrect : this.dataset.textIncorrect;
|
|
2149
|
+
}
|
|
2150
|
+
if (externalScored === "externalMachine") {
|
|
2151
|
+
return Number.isNaN(score) || score === void 0 ? this.dataset.scoreUnknown : `We hebben je antwoord ${score === 0 ? "geen punten" : score == 1 ? "\xE9\xE9n punt" : `${score} punten`} gegeven. Je kunt je score zelf aanpassen als je denkt dat dat niet klopt.`;
|
|
2152
|
+
}
|
|
2153
|
+
if (externalScored === "human") {
|
|
2154
|
+
return Number.isNaN(score) ? "" : "Deze score heb je zelf toegekend.";
|
|
2155
|
+
}
|
|
2156
|
+
return this.dataset.inProgress;
|
|
2157
|
+
};
|
|
2158
|
+
return html22`<div>${feedbackText()}</div>`;
|
|
2159
|
+
}
|
|
2160
|
+
};
|
|
2161
|
+
__decorateClass([
|
|
2162
|
+
consume15({ context: computedContext, subscribe: true })
|
|
2163
|
+
], TestScoringFeedback.prototype, "computedContext", 2);
|
|
2164
|
+
__decorateClass([
|
|
2165
|
+
property17({ type: String, attribute: "view" })
|
|
2166
|
+
], TestScoringFeedback.prototype, "view", 2);
|
|
2167
|
+
TestScoringFeedback = __decorateClass([
|
|
2168
|
+
customElement21("test-scoring-feedback")
|
|
2169
|
+
], TestScoringFeedback);
|
|
2170
|
+
|
|
2171
|
+
// src/lib/qti-test/components/test-check-item.ts
|
|
2172
|
+
import { css as css10, html as html23, LitElement as LitElement23 } from "lit";
|
|
2173
|
+
import { customElement as customElement22 } from "lit/decorators.js";
|
|
2174
|
+
var TestCheckItem = class extends LitElement23 {
|
|
2175
|
+
constructor() {
|
|
2176
|
+
super();
|
|
2177
|
+
this.addEventListener("click", () => {
|
|
2178
|
+
this.dispatchEvent(new CustomEvent("test-end-attempt", { bubbles: true }));
|
|
2179
|
+
this.dispatchEvent(
|
|
2180
|
+
new CustomEvent("test-show-correct-response", {
|
|
2181
|
+
detail: true,
|
|
2182
|
+
bubbles: true
|
|
2183
|
+
})
|
|
2184
|
+
);
|
|
2185
|
+
const qtiTest = this.closest("qti-test");
|
|
2186
|
+
const testContainer = qtiTest.querySelector("test-container");
|
|
2187
|
+
const viewElements = Array.from(testContainer.shadowRoot.querySelectorAll("[view]"));
|
|
2188
|
+
viewElements.forEach((element) => {
|
|
2189
|
+
element.classList.toggle("show", true);
|
|
2190
|
+
});
|
|
2191
|
+
});
|
|
2192
|
+
}
|
|
2193
|
+
render() {
|
|
2194
|
+
return html23` <slot></slot> `;
|
|
2195
|
+
}
|
|
2196
|
+
};
|
|
2197
|
+
TestCheckItem.styles = css10`
|
|
2198
|
+
:host {
|
|
2199
|
+
${btn};
|
|
2200
|
+
}
|
|
2201
|
+
:host([disabled]) {
|
|
2202
|
+
${dis};
|
|
2203
|
+
}
|
|
2204
|
+
`;
|
|
2205
|
+
TestCheckItem = __decorateClass([
|
|
2206
|
+
customElement22("test-check-item")
|
|
2207
|
+
], TestCheckItem);
|
|
2208
|
+
|
|
2209
|
+
export {
|
|
2210
|
+
QtiTest,
|
|
2211
|
+
QtiAssessmentItemRef,
|
|
2212
|
+
QtiAssessmentSection,
|
|
2213
|
+
QtiAssessmentTest,
|
|
2214
|
+
QtiTestPart,
|
|
2215
|
+
QtiTestFeedback,
|
|
2216
|
+
TestNavigation,
|
|
2217
|
+
TestNext,
|
|
2218
|
+
TestPrev,
|
|
2219
|
+
TestView,
|
|
2220
|
+
TestItemLink,
|
|
2221
|
+
TestEndAttempt,
|
|
2222
|
+
TestShowCorrectResponse,
|
|
2223
|
+
TestPagingButtonsStamp,
|
|
2224
|
+
TestContainer,
|
|
2225
|
+
TestPrintVariables,
|
|
2226
|
+
TestSectionButtonsStamp,
|
|
2227
|
+
TestSectionLink,
|
|
2228
|
+
TestPrintContext,
|
|
2229
|
+
TestStamp,
|
|
2230
|
+
TestScoringButtons,
|
|
2231
|
+
TestViewToggle,
|
|
2232
|
+
TestScoringFeedback,
|
|
2233
|
+
TestCheckItem
|
|
2234
|
+
};
|
|
2235
|
+
//# sourceMappingURL=chunk-TJ6ZOT7A.js.map
|