@citolab/tspci 2.3.4 → 2.4.0-alpha.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@citolab/tspci",
3
- "version": "2.3.4",
3
+ "version": "2.4.0-alpha.0",
4
4
  "description": "an opiniated typescript ims-pci builder with tao integration",
5
5
  "author": "patrick <patrick.deklein@cito.nl>",
6
6
  "repository": "@citolab/tspci",
@@ -46,5 +46,5 @@
46
46
  "help": "tspci --help",
47
47
  "init": "tspci init --force --path=../../pcis"
48
48
  },
49
- "gitHead": "93129e8ab54641a8852aa45fc26345e0eaa22c7e"
49
+ "gitHead": "881b3b40f3e31eafdc38ff79b51a16384e67f89d"
50
50
  }
@@ -7,7 +7,7 @@ import Interaction from "./interaction";
7
7
  import style from "./styles.css";
8
8
  import { Configuration, IMSpci, QtiVariableJSON } from "@citolab/tspci";
9
9
  import configProps from "./config.json";
10
- import { actions, StateModel } from "./store";
10
+ import { IStore } from "@citolab/preact-store";
11
11
 
12
12
  type PropTypes = typeof configProps;
13
13
 
@@ -16,7 +16,7 @@ class App implements IMSpci<PropTypes> {
16
16
  config: Configuration<PropTypes>; // reference to the interface of the config object which you get when getInstance is called by the player
17
17
  state: string; // keep a reference to the state
18
18
  shadowdom: ShadowRoot; // Not mandatory, but its wise to create a shadowroot
19
- store: Store<StateModel>;
19
+ store: IStore<StateModel>;
20
20
 
21
21
  private logActions: { type: string; payload: unknown }[] = []; // optional logActions
22
22
 
@@ -30,8 +30,10 @@ class App implements IMSpci<PropTypes> {
30
30
 
31
31
  const initState: StateModel = stateString ? JSON.parse(stateString).state : { input: 0 };
32
32
  this.logActions = stateString ? JSON.parse(stateString).log : [];
33
- this.store = initStore<StateModel>(actions as any[], initState, (action) => this.logActions.push(action));
34
-
33
+ this.store = initStore(this.initialState );
34
+ if (restoredState || logActions) {
35
+ this.store.restoreState(restoredState, logActions);
36
+ }
35
37
  this.shadowdom = dom.attachShadow({ mode: "closed" });
36
38
  this.render();
37
39
 
@@ -1,16 +1,13 @@
1
- import { useStore } from "@citolab/preact-store";
1
+ import { useStore, IStore, Action } from "@citolab/preact-store";
2
2
  import { ActionType, StateModel } from "./store";
3
3
  import configProps from "./config.json";
4
4
  import procenten from "./assets/procenten.png"; // image types are bundled inside the js
5
5
 
6
6
  type PropTypes = typeof configProps;
7
7
 
8
- const Interaction = ({ config, dom }: { config: PropTypes; dom: Document | ShadowRoot }) => {
9
- const [state, dispatch] = useStore<StateModel, ActionType>(
10
- (type, payload) => {
11
- console.log(`type: ${type}, payload: ${JSON.stringify(payload)}`);
12
- }
13
- );
8
+ const Interaction = ({ config, dom }: { config: PropTypes; dom: Document | ShadowRoot, store: IStore<StateModel> }) => {
9
+ const state = useStore(store);
10
+
14
11
  return <div className="pci-container">
15
12
  <h1>{config.title}</h1>
16
13
  <div className="body">
@@ -21,12 +18,12 @@ const Interaction = ({ config, dom }: { config: PropTypes; dom: Document | Shado
21
18
  <input type="number"
22
19
  onKeyUp={(e) => {
23
20
  const input = e.target as HTMLInputElement;
24
- dispatch<{ input: number }>("SET_INPUT", { input: +input.value });
21
+ store.dispatch<{ input: number }>({ type: "SET_INPUT", payload: { input: +input.value } });
22
+ }
25
23
  }
26
- }
27
- onChange={(e) => {
24
+ onChange={(e) => {
28
25
  const input = e.target as HTMLInputElement;
29
- dispatch<{ input: number }>("SET_INPUT", { input: +input.value });
26
+ store.dispatch<{ input: number }>({ type: "SET_INPUT", payload: { input: +input.value } });
30
27
  }} min="0" max="100" />%
31
28
  </div>
32
29
  </div>;
@@ -1,12 +1,15 @@
1
- export type ActionType = "SET_INPUT";
1
+ import { Store } from "@citolab/preact-store";
2
2
 
3
+ export type ActionType = "SET_INPUT" | "RESTORE_STATE";
3
4
  export type StateModel = { input: number };
4
5
 
5
- export const actions = [
6
- {
7
- type: "SET_INPUT" as ActionType,
8
- action: (state: StateModel, { input }: { input: number }) => {
9
- return {...state, input };
10
- },
11
- },
12
- ];
6
+ export const initStore = (initialState: StateModel, restoreData?: {
7
+ state: StateModel;
8
+ actions: { type: string; payload: any; timestamp?: number }[];
9
+ }) => {
10
+ const store = new Store<StateModel>(initialState, restoreData);
11
+ store.addReducer<{ input: number }>("SET_INPUT", (state, { input }) => {
12
+ return {...state, input };
13
+ });
14
+ return store;
15
+ }
package/public/index.html CHANGED
@@ -1,152 +1,203 @@
1
1
  <!DOCTYPE html>
2
2
  <html lang="en">
3
- <head>
4
- <title>Hello RequireJS</title>
5
- <script
6
- src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"
7
- integrity="sha512-c3Nl8+7g4LMSTdrm621y7kf9v3SDPnhxLNhcjFJbKECVnmZHTdo+IRO05sNLTH/D3vA6u1X32ehoLC7WFVdheg=="
8
- crossorigin="anonymous"
9
- referrerpolicy="no-referrer"
10
- ></script>
11
- <!-- <link rel="stylesheet" class="bootswatcher" href="//bootswatch.com/5/lumen/bootstrap.min.css" /> -->
12
-
13
- <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" />
14
-
15
- <style>
16
- html {
17
- font-size: 100%;
18
- }
19
- #pciContainer {
20
- width: 100%;
21
- /* min-width: 800px; */
22
-
23
- max-width: 1024px;
24
- height: 600px;
25
- min-height: 600px;
26
- /* max-height: 600px; */
27
- border: 1px solid #eeeeee;
28
- overflow: hidden;
29
- margin: 0 auto;
30
- background-color: white;
31
- border-radius: 10px;
32
- margin-top: 1rem;
33
- }
34
- #devtools {
35
- max-width: 1024px;
36
- margin: 0 auto;
37
- }
38
- </style>
39
- </head>
40
- <body class="bg-light">
41
- <div class="container">
42
- <div
43
- class="alert alert-danger position-fixed mx-auto bottom-0"
44
- style="display: none"
45
- role="alert"
46
- id="noresponse"
47
- >
48
- user has not yet interacted yet, getResponse should return undefined.
49
- </div>
50
-
51
- <div id="pciContainer" class="shadow-sm">
52
- <div id="loading" class="text-muted mx-auto p-4">Loading...</div>
53
- </div>
54
- <div id="devtools"></div>
3
+
4
+ <head>
5
+ <title>Hello RequireJS</title>
6
+ <script src="https://cdnjs.cloudflare.com/ajax/libs/require.js/2.3.6/require.min.js"
7
+ integrity="sha512-c3Nl8+7g4LMSTdrm621y7kf9v3SDPnhxLNhcjFJbKECVnmZHTdo+IRO05sNLTH/D3vA6u1X32ehoLC7WFVdheg=="
8
+ crossorigin="anonymous" referrerpolicy="no-referrer"></script>
9
+ <!-- <link rel="stylesheet" class="bootswatcher" href="//bootswatch.com/5/lumen/bootstrap.min.css" /> -->
10
+
11
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap@5.1.3/dist/css/bootstrap.min.css" />
12
+
13
+ <style>
14
+ html {
15
+ font-size: 100%;
16
+ }
17
+
18
+ #pciContainer {
19
+ width: 100%;
20
+ /* min-width: 800px; */
21
+
22
+ max-width: 1024px;
23
+ height: 600px;
24
+ min-height: 600px;
25
+ /* max-height: 600px; */
26
+ border: 1px solid #eeeeee;
27
+ overflow: hidden;
28
+ margin: 0 auto;
29
+ background-color: white;
30
+ border-radius: 10px;
31
+ margin-top: 1rem;
32
+ }
33
+
34
+ #devtools {
35
+ max-width: 1024px;
36
+ margin: 0 auto;
37
+ }
38
+ </style>
39
+ </head>
40
+
41
+ <body class="bg-light">
42
+ <div class="container">
43
+ <div class="alert alert-danger position-fixed mx-auto left-2 top-2" style="display: none" role="alert"
44
+ id="noresponse">
45
+ user has not yet interacted yet<br />getResponse should return undefined.
55
46
  </div>
56
47
 
57
- <script type="module">
58
- import { h, Component, render } from "https://unpkg.com/preact@latest?module";
59
- import { useState, useEffect } from "https://unpkg.com/preact@latest/hooks/dist/hooks.module.js?module";
60
- import htm from "https://unpkg.com/htm?module";
61
- // Initialize htm with Preact
62
- const html = htm.bind(h);
63
-
64
- function App({ item }) {
65
- const [show, setShow] = useState(false);
66
-
67
- let keyArray = [];
68
- if (item.config && item.config.properties) {
69
- keyArray = Object.keys(item.config.properties).map((keyName, keyIndex) => ({
70
- key: keyName,
71
- value: item.config.properties[keyName],
72
- }));
73
- }
48
+ <div id="pciContainer" class="shadow-sm">
49
+ <div id="loading" class="text-muted mx-auto p-4">Loading...</div>
50
+ </div>
51
+ <div id="devtools"></div>
52
+ </div>
74
53
 
75
- function resetlocalstorage() {
76
- localStorage.removeItem(item.typeIdentifier + "_state");
77
- localStorage.removeItem(item.typeIdentifier + "_props");
78
- localStorage.removeItem(item.typeIdentifier + "_response");
79
- }
54
+ <script type="module">
55
+ import { h, Component, render } from "https://unpkg.com/preact@latest?module";
56
+ import { useState, useEffect, useRef } from "https://unpkg.com/preact@latest/hooks/dist/hooks.module.js?module";
57
+ import htm from "https://unpkg.com/htm?module";
58
+ // Initialize htm with Preact
59
+ const html = htm.bind(h);
80
60
 
81
- function reset() {
82
- resetlocalstorage();
83
- location.reload();
84
- }
61
+ /**
62
+ * Minified useInterval hook. A custom hook that calls the given `callback` function at the given `delay` interval.
63
+ * useInterval(() => {
64
+ * console.log('interval')
65
+ * }, 1000);
66
+ * The callback is not called immediately on mount but only after the first delay.
67
+ */
68
+ function useInterval(c, d) { const s = useRef(); useEffect(() => { s.current = c }, [c]), useEffect(() => { if (d !== null) { const i = setInterval(() => { s.current() }, d); return () => clearInterval(i) } }, [d]) }
85
69
 
86
- function resetResponse() {
87
- localStorage.removeItem(item.typeIdentifier + "_response");
88
- item.resetResponse();
89
- }
70
+ function App({ item }) {
71
+ const [show, setShow] = useState(false);
90
72
 
91
- function setResponse() {
92
- const response = localStorage.getItem(item.typeIdentifier + "_response");
93
- if (response) {
94
- item.setResponse({
95
- base: {
96
- string: response,
97
- },
98
- });
99
- } else {
100
- alert("no response stored");
101
- }
102
- }
73
+ const [range, setRange] = useState(0);
74
+ const [currentRangeValue, setCurrentRangeValue] = useState(0);
75
+ const [maxRange, setMaxRange] = useState(0);
76
+ const [actions, setActions] = useState([]);
77
+
78
+ const isReplaying = useRef(false);
79
+ const replayedIndex = useRef(0);
80
+ const store = item.store;
103
81
 
104
- function saveResponse() {
105
- localStorage.setItem(item.typeIdentifier + "_response", item.getResponse().base.string);
82
+ useEffect(() => {
83
+ if (store) {
84
+ store.subscribeActions(action => {
85
+ if (!isReplaying.current) {
86
+ const allActions = store.actions;
87
+ setRange(allActions.length);
88
+ setMaxRange(allActions.length);
89
+ setActions(allActions);
90
+ setCurrentRangeValue(allActions.findIndex(a => a === action) + 1);
91
+ } else {
92
+ replayedIndex.current++;
93
+ setCurrentRangeValue(replayedIndex.current);
94
+ }
95
+ });
106
96
  }
97
+ }, [store]);
107
98
 
108
- function compareResponse() {
109
- const response = localStorage.getItem(item.typeIdentifier + "_response");
110
- if (!response) {
111
- alert("no response stored");
112
- return;
113
- }
114
- if (response === item.getResponse().base.string) {
115
- alert("same");
116
- } else {
117
- alert("different");
99
+ async function scrubRange(value) {
100
+ if (!isReplaying.current) {
101
+ setCurrentRangeValue(value);
102
+ resetResponse();
103
+ if (store) {
104
+ isReplaying.current = true;
105
+ replayedIndex.current = 0;
106
+ setCurrentRangeValue(value);
107
+ await store.replay([...actions], { until: value, animate: false });
108
+ isReplaying.current = false;
118
109
  }
119
110
  }
111
+ }
120
112
 
121
- function reload() {
122
- const state = item.getState();
123
- localStorage.setItem(item.typeIdentifier + "_state", JSON.stringify(state));
124
- location.reload();
125
- }
113
+ let keyArray = [];
114
+ if (item.config && item.config.properties) {
115
+ keyArray = Object.keys(item.config.properties).map((keyName, keyIndex) => ({
116
+ key: keyName,
117
+ value: item.config.properties[keyName],
118
+ }));
119
+ }
126
120
 
127
- function playState() {
128
- const state = item.getState();
129
- item.store.replay(state.log); // state.log
130
- }
121
+ function resetlocalstorage() {
122
+ localStorage.removeItem(item.typeIdentifier + "_state");
123
+ localStorage.removeItem(item.typeIdentifier + "_props");
124
+ localStorage.removeItem(item.typeIdentifier + "_response");
125
+ }
126
+
127
+ function reset() {
128
+ resetlocalstorage();
129
+ location.reload();
130
+ }
131
131
 
132
- function changeConfig(key, value) {
133
- item.config.properties[key] = value;
134
- item.trigger(`change${key}`, value);
132
+ function resetResponse() {
133
+ localStorage.removeItem(item.typeIdentifier + "_response");
134
+ item.resetResponse();
135
+ }
136
+
137
+ function setResponse() {
138
+ const response = localStorage.getItem(item.typeIdentifier + "_response");
139
+ if (response) {
140
+ item.setResponse({
141
+ base: {
142
+ string: response,
143
+ },
144
+ });
145
+ } else {
146
+ alert("no response stored");
135
147
  }
148
+ }
149
+
150
+ function saveResponse() {
151
+ localStorage.setItem(item.typeIdentifier + "_response", item.getResponse().base.string);
152
+ }
136
153
 
137
- function reConfig(key, value) {
138
- localStorage.setItem(item.typeIdentifier + "_props", JSON.stringify(item.config.properties));
139
- location.reload();
154
+ function compareResponse() {
155
+ const response = localStorage.getItem(item.typeIdentifier + "_response");
156
+ if (!response) {
157
+ alert("no response stored");
158
+ return;
159
+ }
160
+ if (response === item.getResponse().base.string) {
161
+ alert("same");
162
+ } else {
163
+ alert("different");
140
164
  }
165
+ }
166
+
167
+ function reload() {
168
+ const state = item.getState();
169
+ localStorage.setItem(item.typeIdentifier + "_state", JSON.stringify(state));
170
+ location.reload();
171
+ }
141
172
 
142
- function unload() {
143
- item.destroy && item.destroy();
144
- item.oncompleted && item.oncompleted();
145
- var name = require.undef("index.js");
146
- loadItem();
173
+ async function playState() {
174
+ if (store && !isReplaying.current) {
175
+ resetResponse();
176
+ isReplaying.current = true;
177
+ replayedIndex.current = 0;
178
+ await store.replay([...actions]);
179
+ isReplaying.current = false;
147
180
  }
181
+ }
182
+
183
+ function changeConfig(key, value) {
184
+ item.config.properties[key] = value;
185
+ item.trigger(`change${key}`, value);
186
+ }
148
187
 
149
- return html`
188
+ function reConfig(key, value) {
189
+ localStorage.setItem(item.typeIdentifier + "_props", JSON.stringify(item.config.properties));
190
+ location.reload();
191
+ }
192
+
193
+ function unload() {
194
+ item.destroy && item.destroy();
195
+ item.oncompleted && item.oncompleted();
196
+ var name = require.undef("index.js");
197
+ loadItem();
198
+ }
199
+
200
+ return html`
150
201
  <div>
151
202
  <div style="width:300px" class="shadow offcanvas offcanvas-end ${show ? "show visible" : ""}">
152
203
  <div class="offcanvas-header">
@@ -161,7 +212,7 @@
161
212
  <div class="offcanvas-body">
162
213
  <ul class="card-body">
163
214
  ${keyArray.map(
164
- ({ key, value }) => html`<div>
215
+ ({ key, value }) => html`<div>
165
216
  <div>${key}</div>
166
217
  <input
167
218
  type="text"
@@ -170,27 +221,21 @@
170
221
  onkeyup="${(e) => changeConfig(key, e.target.value)}"
171
222
  />
172
223
  </div>`
173
- )}
224
+ )}
174
225
  </ul>
175
226
  <button class="btn btn-primary" onClick="${() => reConfig()}">save and reload</button>
176
227
  </div>
177
228
  </div>
178
229
 
179
- <div class="d-flex justify-content-start mt-3" role="toolbar" aria-label="Toolbar with button groups">
180
- <div>
230
+ <div class="d-flex justify-content-between mt-3" role="toolbar" aria-label="Toolbar with button groups">
231
+ <div class="d-flex flex-column">
181
232
  <small class="text-muted">configuration</small>
182
233
  <div class="btn-group me-2 shadow-sm" role="group" aria-label="Basic checkbox toggle button group">
183
- <button class="btn btn-outline-secondary" onClick="${() => setShow(true)}">props</button>
184
- <button
185
- class="btn btn-outline-secondary"
186
- onClick="${() => alert(JSON.stringify(item.config), null, 4)}"
187
- >
188
- config
189
- </button>
234
+ <button class="btn btn-outline-secondary" onClick="${() => setShow(true)}">config</button>
190
235
  </div>
191
236
  </div>
192
237
 
193
- <div>
238
+ <div class="d-flex flex-column">
194
239
  <small class="text-muted">data</small>
195
240
  <div class="btn-group me-2 shadow-sm" role="group" aria-label="Basic checkbox toggle button group">
196
241
  <button
@@ -203,29 +248,30 @@
203
248
  </div>
204
249
  </div>
205
250
 
206
- <div>
251
+ <div class="d-flex flex-column">
207
252
  <small class="text-muted">state</small>
208
253
  <div class="btn-group me-2 shadow-sm" role="group" aria-label="Basic checkbox toggle button group">
209
254
  <button class="btn btn-outline-secondary" onClick="${() => reload()}">reload</button>
210
255
  </div>
211
256
  </div>
212
- <div>
213
- <small class="text-muted">responses</small>
257
+
258
+ <div class="d-flex flex-column">
259
+ <small class="text-muted">scoring TAO (match correct string)</small>
214
260
  <div class="btn-group me-2 shadow-sm" role="group" aria-label="Basic checkbox toggle button group">
215
- <button class="btn btn-outline-secondary" onClick="${() => saveResponse()}">store</button>
261
+ <button class="btn btn-outline-secondary" onClick="${() => saveResponse()}">save</button>
216
262
  <button class="btn btn-outline-secondary" onClick="${() => compareResponse()}">compare</button>
217
- <button class="btn btn-outline-secondary" onClick="${() => setResponse()}">set</button>
263
+ <button class="btn btn-outline-secondary" onClick="${() => setResponse()}">load</button>
218
264
  <button
219
265
  class="btn btn-outline-secondary"
220
266
  disabled="${!item.resetResponse}"
221
267
  onClick="${() => resetResponse()}"
222
268
  >
223
- reset
269
+ clear
224
270
  </button>
225
271
  </div>
226
272
  </div>
227
273
 
228
- <div>
274
+ <div class="d-flex flex-column">
229
275
  <small class="text-muted">ㅤ</small>
230
276
  <div class="d-flex">
231
277
  <button
@@ -239,80 +285,91 @@
239
285
  </div>
240
286
  </div>
241
287
  </div>
242
-
243
- <div class="mt-2 d-flex gap-2">
244
- <button class="btn btn-outline-secondary" onClick="${() => playState()}">play</button>
245
- <input
246
- class="form-control-range flex-grow-1"
247
- type="range"
248
- min="0"
249
- max="${JSON.parse(item.getState()).log.length}"
250
- value=${range}
251
- onChange=${(e) => setRange(e.target.value)}
252
- step="1"
253
- />
254
- </div>
288
+ ${store && html`<div class="mt-5 d-flex flex-column gap-2">
289
+ <div class="d-flex justify-content-between">
290
+ <button class="btn btn-outline-secondary" onClick="${() => playState()}">
291
+ <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-play" viewBox="0 0 16 16">
292
+ <path d="M3.905 14.21a1 1 0 0 1-1.555-.832V2.622a1 1 0 0 1 1.555-.832l9.066 5.293a1 1 0 0 1 0 1.664l-9.066 5.293zm-.57-1.664L11.4 8 3.335 2.454v10.092z"/>
293
+ </svg>
294
+ </button>
295
+ <span>${currentRangeValue}</span>
296
+ </div>
297
+ <div class="d-flex gap-2">
298
+ <span>0</span>
299
+ <input
300
+ class="form-control-range flex-grow-1"
301
+ type="range"
302
+ min="0"
303
+ max="${maxRange}"
304
+ value=${currentRangeValue}
305
+ onInput=${(e) => scrubRange(e.target.value)}
306
+ step="1"
307
+ />
308
+ <span>${maxRange}</span>
309
+ </div>
310
+ </div>` }
255
311
 
256
312
  </div>
257
313
  `;
314
+ }
315
+
316
+ function register(item) {
317
+ let state = undefined;
318
+ const localStorageState = localStorage.getItem(item.typeIdentifier + "_state");
319
+ if (localStorageState) {
320
+ state = JSON.parse(localStorageState);
258
321
  }
322
+ let props = {};
323
+ const localStorageProps = localStorage.getItem(item.typeIdentifier + "_props");
324
+ if (localStorageProps) {
325
+ props = JSON.parse(localStorageProps);
326
+ }
327
+ const dom = document.getElementById("pciContainer");
328
+ const config = {
329
+ onready: () => {
330
+ document.getElementById("loading").innerHTML = "";
331
+ },
332
+ properties: props,
333
+ };
259
334
 
260
- function register(item) {
261
- let state = undefined;
262
- const localStorageState = localStorage.getItem(item.typeIdentifier + "_state");
263
- if (localStorageState) {
264
- state = JSON.parse(localStorageState);
265
- }
266
- let props = {};
267
- const localStorageProps = localStorage.getItem(item.typeIdentifier + "_props");
268
- if (localStorageProps) {
269
- props = JSON.parse(localStorageProps);
270
- }
271
- const dom = document.getElementById("pciContainer");
272
- const config = {
273
- onready: () => {
274
- document.getElementById("loading").innerHTML = "";
275
- },
276
- properties: props,
277
- };
278
-
279
- item.getInstance(dom, config, state);
280
-
281
- if (item.getResponse() !== undefined) {
282
- document.getElementById("noresponse").style.display = "block";
283
- }
284
- localStorage.removeItem(item.typeIdentifier + "_state");
335
+ item.getInstance(dom, config, state);
336
+
337
+ if (item.getResponse() !== undefined) {
338
+ document.getElementById("noresponse").style.display = "block";
285
339
  }
340
+ localStorage.removeItem(item.typeIdentifier + "_state");
341
+ }
286
342
 
287
- define("qtiCustomInteractionContext", (qtiCustomInteractionContext) => {
288
- return { register: (ctxA) => register(ctxA) };
343
+ define("qtiCustomInteractionContext", (qtiCustomInteractionContext) => {
344
+ return { register: (ctxA) => register(ctxA) };
345
+ });
346
+
347
+ function loadItem() {
348
+ requirejs(["index.js"], (itemInstance) => {
349
+ render(html`<${App} item="${itemInstance}" />`, document.getElementById("devtools"));
289
350
  });
351
+ }
352
+ window.onload = () => loadItem();
353
+ </script>
290
354
 
291
- function loadItem() {
292
- requirejs(["index.js"], (itemInstance) => {
293
- render(html`<${App} item="${itemInstance}" />`, document.getElementById("devtools"));
294
- });
295
- }
296
- window.onload = () => loadItem();
297
- </script>
298
-
299
- <h3 style="margin-top: 12rem">checklist</h3>
300
- <ul style="margin-bottom: 10rem">
301
- <li>Is the css scoped to the item?</li>
302
- <li>Is the css/item somewhat responsive on smaller screens</li>
303
- <li>Can you reset the item to its initial state</li>
304
- <li>Can it restore its state ( reload )</li>
305
- <li>Can you change the config</li>
306
- <li>Colorblind</li>
307
- <li>Touch/Mouse friendly</li>
308
- <li>Works in older browser like IE11</li>
309
- <li>Destroys itself correctly, removing event listeners</li>
310
- <li>No objects or variables on global scope</li>
311
- <li>Triggers correctly on changed config properties</li>
312
- <li>Implements on off() methods of tao?</li>
313
- <li>Response type and response correct</li>
314
- <li>getResponse() should give undefined when user has not done anything</li>
315
- <li>For TAO to enter response is the entered comparable by the response given</li>
316
- </ul>
317
- </body>
318
- </html>
355
+ <h3 style="margin-top: 12rem">checklist</h3>
356
+ <ul style="margin-bottom: 10rem">
357
+ <li>Is the css scoped to the item?</li>
358
+ <li>Is the css/item somewhat responsive on smaller screens</li>
359
+ <li>Can you reset the item to its initial state</li>
360
+ <li>Can it restore its state ( reload )</li>
361
+ <li>Can you change the config</li>
362
+ <li>Colorblind</li>
363
+ <li>Touch/Mouse friendly</li>
364
+ <li>Works in older browser like IE11</li>
365
+ <li>Destroys itself correctly, removing event listeners</li>
366
+ <li>No objects or variables on global scope</li>
367
+ <li>Triggers correctly on changed config properties</li>
368
+ <li>Implements on off() methods of tao?</li>
369
+ <li>Response type and response correct</li>
370
+ <li>getResponse() should give undefined when user has not done anything</li>
371
+ <li>For TAO to enter response is the entered comparable by the response given</li>
372
+ </ul>
373
+ </body>
374
+
375
+ </html>