@examind/block-sdk 0.1.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/dist/index.js ADDED
@@ -0,0 +1,2083 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ exportToHtml: () => exportToHtml,
24
+ importFromHtml: () => importFromHtml
25
+ });
26
+ module.exports = __toCommonJS(index_exports);
27
+
28
+ // src/utils/NodeHandlerChainBuilder.ts
29
+ var NodeHandlerChainBuilder = class {
30
+ constructor() {
31
+ this.handlerConstructors = [];
32
+ }
33
+ addHandler(handlerConstructor) {
34
+ this.handlerConstructors.push(handlerConstructor);
35
+ return this;
36
+ }
37
+ build() {
38
+ if (this.handlerConstructors.length === 0) {
39
+ throw new Error(
40
+ "No handlers have been added to the chain. Call addHandler() at least once."
41
+ );
42
+ }
43
+ const firstHandler = new this.handlerConstructors[0]();
44
+ let currentHandler = firstHandler;
45
+ for (let i = 1; i < this.handlerConstructors.length; i++) {
46
+ const nextHandlerInstance = new this.handlerConstructors[i]();
47
+ currentHandler.setNext(nextHandlerInstance);
48
+ currentHandler = nextHandlerInstance;
49
+ }
50
+ return firstHandler;
51
+ }
52
+ };
53
+
54
+ // src/typeGuards/isSerializedEssayQuestionNode.ts
55
+ function isSerializedEssayQuestionNode(node) {
56
+ return node?.type === "essay-question" && "id" in node && typeof node.id === "string" && "points" in node && typeof node.points === "number" && "maxWords" in node && typeof node.maxWords === "number" && "hideChat" in node && typeof node.hideChat === "boolean";
57
+ }
58
+
59
+ // src/exportToHtml/types.ts
60
+ var NodeHandler = class {
61
+ constructor() {
62
+ this.nextHandler = null;
63
+ }
64
+ setNext(handler) {
65
+ this.nextHandler = handler;
66
+ return handler;
67
+ }
68
+ handle(node, metadata) {
69
+ const result = this.processNode(node, metadata);
70
+ if (result) {
71
+ return result;
72
+ } else if (this.nextHandler) {
73
+ return this.nextHandler.handle(node, metadata);
74
+ }
75
+ return null;
76
+ }
77
+ };
78
+
79
+ // src/exportToHtml/EssayQuestionNodeHandler.ts
80
+ var EssayQuestionNodeHandler = class extends NodeHandler {
81
+ processNode(node) {
82
+ if (!isSerializedEssayQuestionNode(node)) return null;
83
+ const attributes = [
84
+ `data-x-points="${node.points}"`,
85
+ `data-x-max-words="${node.maxWords}"`,
86
+ `data-x-hide-chat="${node.hideChat}"`,
87
+ node.aiChatModel ? `data-x-ai-chat-model="${node.aiChatModel}"` : null
88
+ ].filter(Boolean).join(" ");
89
+ const aiSystemMessageTag = node.aiSystemMessage ? `<x-ai-system-message>${node.aiSystemMessage}</x-ai-system-message>` : "";
90
+ return `<x-essay id="${node.id}" data-x-prompt ${attributes.trimEnd()}>${aiSystemMessageTag}</x-essay>`;
91
+ }
92
+ };
93
+
94
+ // src/typeGuards/isSerializedFillInTheBlankQuestionNode.ts
95
+ function isSerializedFillInTheBlankQuestionNode(node) {
96
+ return node?.type === "fill-in-the-blank-question" && "id" in node && typeof node.id === "string" && "content" in node && "pointsPerSpace" in node && typeof node.pointsPerSpace === "number";
97
+ }
98
+
99
+ // src/typeGuards/isSerializedParagraphNode.ts
100
+ function isSerializedParagraphNode(node) {
101
+ return node?.type === "paragraph" && "children" in node && Array.isArray(node.children);
102
+ }
103
+
104
+ // src/exportToHtml/createHtmlFromNestedNodes.ts
105
+ function createHtmlFromNestedNodes(nodes, metadata) {
106
+ const children = [];
107
+ let prevWasParagraph = false;
108
+ nodes.forEach((child) => {
109
+ const isParagraph = isSerializedParagraphNode(child);
110
+ const element = traverse(child, metadata);
111
+ if (element) {
112
+ if (prevWasParagraph && isParagraph) {
113
+ children.push("<br>");
114
+ }
115
+ children.push(element);
116
+ prevWasParagraph = isParagraph;
117
+ }
118
+ });
119
+ return children.join("");
120
+ }
121
+
122
+ // src/exportToHtml/createHtmlFromNestedEditor.ts
123
+ function createHtmlFromNestedEditor(editor, metadata) {
124
+ const rootNode = editor?.editorState?.root;
125
+ if (!rootNode) return "";
126
+ return createHtmlFromNestedNodes(rootNode.children, metadata);
127
+ }
128
+
129
+ // src/exportToHtml/FillInTheBlankQuestionNodeHandler.ts
130
+ var FillInTheBlankQuestionNodeHandler = class extends NodeHandler {
131
+ processNode(node) {
132
+ if (!isSerializedFillInTheBlankQuestionNode(node)) return null;
133
+ return `<x-fill-in-the-blank id="${node.id}" data-x-prompt data-x-points-per-space="${node.pointsPerSpace}">${createHtmlFromNestedEditor(node.content, { parentNode: node })}</x-fill-in-the-blank>`;
134
+ }
135
+ };
136
+
137
+ // src/typeGuards/isSerializedFillInTheBlankSpaceNode.ts
138
+ function isSerializedFillInTheBlankSpaceNode(node) {
139
+ return node?.type === "space" && "id" in node && typeof node.id === "string" && "spaceType" in node && "matches" in node && typeof node.matches === "string";
140
+ }
141
+
142
+ // src/exportToHtml/FillInTheBlankSpaceNodeHandler.ts
143
+ var FillInTheBlankSpaceNodeHandler = class extends NodeHandler {
144
+ processNode(node) {
145
+ if (!isSerializedFillInTheBlankSpaceNode(node)) return null;
146
+ const spaceNode = node;
147
+ const baseAttributes = this.getBaseAttributes(spaceNode);
148
+ const spaceTypeAttributes = this.getSpaceTypeAttributes(spaceNode);
149
+ return `<x-space ${baseAttributes}${spaceTypeAttributes}>${spaceNode.spaceName}</x-space>`;
150
+ }
151
+ getBaseAttributes(node) {
152
+ const attributes = [
153
+ `id="${node.id}"`,
154
+ `data-x-type="${node.spaceType}"`,
155
+ `data-x-matches="${node.matches}"`,
156
+ `data-x-case-sensitive="${!!node.caseSensitive}"`,
157
+ `data-x-errors-allowed="${node.errorsAllowed ?? 0}"`,
158
+ `data-x-min-decimals="${node.minDecimals ?? ""}"`,
159
+ `data-x-error-tolerance="${node.errorTolerance ?? 0}"`,
160
+ `data-x-distractors="${node.distractors ?? ""}"`
161
+ ];
162
+ return attributes.join(" ");
163
+ }
164
+ getSpaceTypeAttributes(node) {
165
+ switch (node.spaceType) {
166
+ case "Text":
167
+ return "";
168
+ case "Number":
169
+ return "";
170
+ case "Dropdown":
171
+ return "";
172
+ default:
173
+ return "";
174
+ }
175
+ }
176
+ };
177
+
178
+ // src/typeGuards/isSerializedFinancialStatementQuestionNode.ts
179
+ function isSerializedFinancialStatementQuestionNode(node) {
180
+ return node?.type === "financial-statement-question" && "id" in node && typeof node.id === "string" && "header" in node && "rows" in node && Array.isArray(node.rows) && "points" in node && typeof node.points === "number";
181
+ }
182
+
183
+ // src/exportToHtml/FinancialStatementQuestionNodeHandler.ts
184
+ var isHeading = (row) => {
185
+ return row.type === "Heading";
186
+ };
187
+ var isLine = (row) => {
188
+ return row.type === "Line";
189
+ };
190
+ var FinancialStatementQuestionNodeHandler = class extends NodeHandler {
191
+ processNode(node) {
192
+ if (!isSerializedFinancialStatementQuestionNode(node))
193
+ return null;
194
+ const children = [
195
+ this.processHeaderSection(node),
196
+ this.processRowsSection(node),
197
+ this.processDistractorsSection(node)
198
+ ];
199
+ return `<x-financial-statement id="${node.id}" data-x-prompt data-x-points="${node.points}">${children.join("")}</x-financial-statement>`;
200
+ }
201
+ processHeaderSection(node) {
202
+ return `<x-header>${createHtmlFromNestedEditor(node.header, {
203
+ parentNode: node
204
+ })}${this.processColumnHeadersSection(node)}</x-header>`;
205
+ }
206
+ processColumnHeadersSection(node) {
207
+ if (node.columnHeaders.length < 2) return "";
208
+ return `<div style="display:flex; width:100%; margin-top: 16px;"><div style="flex:1; font-weight:bold; text-align:left;">${node.columnHeaders[0]}</div><div style="flex:1; font-weight:bold; text-align:right;">${node.columnHeaders[1]}</div></div>`;
209
+ }
210
+ processRowsSection(node) {
211
+ const rowElements = [];
212
+ for (const row of node.rows) {
213
+ if (isHeading(row)) {
214
+ const isHintAttr = row.isHint ? ' data-x-is-hint="true"' : "";
215
+ rowElements.push(
216
+ `<x-heading id="${row.id}" data-x-depth="${row.depth}"${isHintAttr}>${row.entry}</x-heading>`
217
+ );
218
+ } else if (isLine(row)) {
219
+ const entryHintAttr = row.isEntryHint ? ' data-x-is-entry-hint="true"' : "";
220
+ const amountHintAttr = row.isAmountHint ? ' data-x-is-amount-hint="true"' : "";
221
+ rowElements.push(
222
+ `<x-line id="${row.id}" data-x-depth="${row.depth}"${entryHintAttr}${amountHintAttr}>`,
223
+ `<x-entry>${row.entry}</x-entry>`,
224
+ `<x-amount>${createHtmlFromNestedEditor(row.amount, {
225
+ parentNode: node
226
+ })}</x-amount>`,
227
+ `</x-line>`
228
+ );
229
+ }
230
+ }
231
+ return `<x-rows>${rowElements.join("")}</x-rows>`;
232
+ }
233
+ processDistractorsSection(node) {
234
+ if (!node.distractors || node.distractors.length === 0) {
235
+ return "";
236
+ }
237
+ const distractorElements = node.distractors.map(
238
+ (distractor) => `<x-distractor id="${distractor.id}">${distractor.entry}</x-distractor>`
239
+ );
240
+ return `<x-distractors>${distractorElements.join("")}</x-distractors>`;
241
+ }
242
+ };
243
+
244
+ // src/typeGuards/isSerializedHorizontalRuleNode.ts
245
+ var isSerializedHorizontalRuleNode = (node) => {
246
+ return node.type === "horizontalrule";
247
+ };
248
+
249
+ // src/exportToHtml/HorizontalRuleNodeHandler.ts
250
+ var HorizontalRuleNodeHandler = class extends NodeHandler {
251
+ processNode(node) {
252
+ if (!isSerializedHorizontalRuleNode(node)) return null;
253
+ return "<hr />";
254
+ }
255
+ };
256
+
257
+ // src/typeGuards/isSerializedImageNode.ts
258
+ var isSerializedImageNode = (node) => {
259
+ return node.type === "image";
260
+ };
261
+
262
+ // src/exportToHtml/ImageNodeHandler.ts
263
+ var ImageNodeHandler = class extends NodeHandler {
264
+ processNode(node) {
265
+ if (!isSerializedImageNode(node)) return null;
266
+ const attributes = [
267
+ `src="${node.src}"`,
268
+ node.altText ? `alt="${node.altText}"` : null,
269
+ node.width ? `width="${node.width}"` : null,
270
+ node.align !== "left" ? `align="${node.align}"` : null
271
+ ].filter(Boolean).join(" ");
272
+ return `<img ${attributes} />`;
273
+ }
274
+ };
275
+
276
+ // src/typeGuards/isSerializedJournalEntryQuestionNode.ts
277
+ function isSerializedJournalEntryQuestionNode(node) {
278
+ return node?.type === "journal-entry-question" && "id" in node && typeof node.id === "string" && "lineItems" in node && Array.isArray(node.lineItems) && "points" in node && typeof node.points === "number";
279
+ }
280
+
281
+ // src/exportToHtml/JournalEntryQuestionNodeHandler.ts
282
+ var JournalEntryQuestionNodeHandler = class extends NodeHandler {
283
+ processNode(node) {
284
+ if (!isSerializedJournalEntryQuestionNode(node)) return null;
285
+ const journalNode = node;
286
+ const attributes = [
287
+ `id="${journalNode.id}"`,
288
+ "data-x-prompt",
289
+ `data-x-points="${journalNode.points}"`
290
+ ];
291
+ if (journalNode.errorTolerance !== void 0) {
292
+ attributes.push(
293
+ `data-x-error-tolerance="${journalNode.errorTolerance}"`
294
+ );
295
+ }
296
+ switch (journalNode.journalType) {
297
+ case "default":
298
+ attributes.push('data-x-no-entry-required-correct="false"');
299
+ attributes.push(
300
+ 'data-x-no-entry-required-distractor="false"'
301
+ );
302
+ break;
303
+ case "noEntryRequiredCorrect":
304
+ attributes.push('data-x-no-entry-required-correct="true"');
305
+ attributes.push(
306
+ 'data-x-no-entry-required-distractor="false"'
307
+ );
308
+ break;
309
+ case "noEntryRequiredDistractor":
310
+ attributes.push('data-x-no-entry-required-correct="false"');
311
+ attributes.push('data-x-no-entry-required-distractor="true"');
312
+ break;
313
+ }
314
+ const lineItems = this.processLineItems(journalNode);
315
+ const distractors = this.processDistractors(journalNode);
316
+ return `<x-journal-entry ${attributes.join(" ")}>${lineItems}${distractors}</x-journal-entry>`;
317
+ }
318
+ processLineItems(node) {
319
+ return node.lineItems.filter((item) => item.correct).map(
320
+ (item) => this.createLineItemHtml(item, { parentNode: node })
321
+ ).join("");
322
+ }
323
+ createLineItemHtml(item, metadata) {
324
+ if (!item.correct) return "";
325
+ const accountContent = createHtmlFromNestedEditor(
326
+ item.account,
327
+ metadata
328
+ );
329
+ const debitContent = item.debit ? createHtmlFromNestedEditor(item.debit, metadata) : "";
330
+ const creditContent = item.credit ? createHtmlFromNestedEditor(item.credit, metadata) : "";
331
+ return `<x-line-item id="${item.id}"><x-account>${accountContent}</x-account><x-debit>${debitContent}</x-debit><x-credit>${creditContent}</x-credit></x-line-item>`;
332
+ }
333
+ processDistractors(node) {
334
+ const distractors = node.lineItems.filter((item) => !item.correct).map((item) => {
335
+ const accountContent = createHtmlFromNestedEditor(
336
+ item.account,
337
+ { parentNode: node }
338
+ );
339
+ return `<x-distractor id="${item.id}">${accountContent}</x-distractor>`;
340
+ }).join("");
341
+ return distractors;
342
+ }
343
+ };
344
+
345
+ // src/typeGuards/isSerializedLineBreakNode.ts
346
+ function isSerializedLineBreakNode(node) {
347
+ return node?.type === "linebreak";
348
+ }
349
+
350
+ // src/exportToHtml/LineBreakNodeHandler.ts
351
+ var LineBreakNodeHandler = class extends NodeHandler {
352
+ processNode(node) {
353
+ if (!isSerializedLineBreakNode(node)) return null;
354
+ return `<br>`;
355
+ }
356
+ };
357
+
358
+ // src/typeGuards/isSerializedLinkNode.ts
359
+ var isSerializedLinkNode = (node) => {
360
+ return node.type === "link";
361
+ };
362
+
363
+ // src/exportToHtml/LinkNodeHandler.ts
364
+ var LinkNodeHandler = class extends NodeHandler {
365
+ processNode(node) {
366
+ if (!isSerializedLinkNode(node)) return null;
367
+ const childrenHtml = node.children.map((child) => traverse(child)).filter(Boolean).join("");
368
+ const relAttr = node.rel ? ` rel="${node.rel}"` : "";
369
+ const targetAttr = node.target ? ` target="${node.target}"` : "";
370
+ const titleAttr = node.title ? ` title="${node.title}"` : "";
371
+ return `<a href="${node.url}"${relAttr}${targetAttr}${titleAttr}>${childrenHtml}</a>`;
372
+ }
373
+ };
374
+
375
+ // src/typeGuards/isSerializedListItemNode.ts
376
+ var isSerializedListItemNode = (node) => {
377
+ return node.type === "listitem";
378
+ };
379
+
380
+ // src/exportToHtml/ListItemNodeHandler.ts
381
+ var ListItemNodeHandler = class extends NodeHandler {
382
+ processNode(node, metadata) {
383
+ if (!isSerializedListItemNode(node)) return null;
384
+ const children = [];
385
+ node.children.forEach((child) => {
386
+ const childString = traverse(child, {
387
+ ...metadata,
388
+ parentNode: node
389
+ });
390
+ if (childString) {
391
+ children.push(childString);
392
+ }
393
+ });
394
+ return `<li>${children.join("")}</li>`;
395
+ }
396
+ };
397
+
398
+ // src/typeGuards/isSerializedListNode.ts
399
+ var isSerializedListNode = (node) => {
400
+ return node !== void 0 && node.type === "list";
401
+ };
402
+
403
+ // src/exportToHtml/ListNodeHandler.ts
404
+ var ListNodeHandler = class extends NodeHandler {
405
+ processNode(node, metadata) {
406
+ if (!isSerializedListNode(node)) return null;
407
+ const tag = node.tag;
408
+ const children = [];
409
+ node.children.forEach((child) => {
410
+ const childString = traverse(child, {
411
+ ...metadata,
412
+ parentNode: node
413
+ });
414
+ if (childString) {
415
+ children.push(childString);
416
+ }
417
+ });
418
+ return `<${tag}>${children.join("")}</${tag}>`;
419
+ }
420
+ };
421
+
422
+ // src/typeGuards/isSerializedMatchingQuestionNode.ts
423
+ function isSerializedMatchingQuestionNode(node) {
424
+ return node?.type === "matching-question" && "id" in node && typeof node.id === "string" && "items" in node && Array.isArray(node.items) && "pointsPerMatch" in node && typeof node.pointsPerMatch === "number";
425
+ }
426
+
427
+ // src/exportToHtml/MatchingQuestionNodeHandler.ts
428
+ var MatchingQuestionNodeHandler = class extends NodeHandler {
429
+ processNode(node) {
430
+ if (!isSerializedMatchingQuestionNode(node)) return null;
431
+ const matchingNode = node;
432
+ const attributes = [
433
+ `id="${matchingNode.id}"`,
434
+ "data-x-prompt",
435
+ `data-x-points-per-match="${matchingNode.pointsPerMatch}"`
436
+ ];
437
+ return `<x-matching ${attributes.join(" ")}>${this.createMatchesHtml(matchingNode)}${this.createDistractorsHtml(matchingNode)}</x-matching>`;
438
+ }
439
+ createMatchesHtml(node) {
440
+ const metadata = { parentNode: node };
441
+ return node.items.filter((item) => item.correct).map(
442
+ (item) => `<x-match><x-premise id="${item.id}">${createHtmlFromNestedEditor(
443
+ item.itemPremiseContent,
444
+ metadata
445
+ )}</x-premise><x-option id="${item.id}-option">${createHtmlFromNestedEditor(
446
+ item.itemOptionContent,
447
+ metadata
448
+ )}</x-option></x-match>`
449
+ ).join("");
450
+ }
451
+ createDistractorsHtml(node) {
452
+ const metadata = { parentNode: node };
453
+ return node.items.filter((item) => !item.correct).map(
454
+ (item) => `<x-distractor id="${item.id}">${createHtmlFromNestedEditor(
455
+ item.itemOptionContent,
456
+ metadata
457
+ )}</x-distractor>`
458
+ ).join("");
459
+ }
460
+ };
461
+
462
+ // src/typeGuards/isSerializedMultipleOptionQuestionNode.ts
463
+ function isSerializedMultipleOptionQuestionNode(node) {
464
+ return node?.type === "multiple-option-question" && "id" in node && typeof node.id === "string" && "options" in node && Array.isArray(node.options) && "questionType" in node && typeof node.questionType === "string" && "points" in node && typeof node.points === "number";
465
+ }
466
+
467
+ // src/exportToHtml/MultipleOptionQuestionNodeHandler.ts
468
+ var MultipleOptionQuestionNodeHandler = class extends NodeHandler {
469
+ processNode(node) {
470
+ if (!isSerializedMultipleOptionQuestionNode(node)) return null;
471
+ if (node.questionType === "multiple-choice") {
472
+ return this.processMultipleChoiceNode(node);
473
+ } else if (node.questionType === "multiple-answers") {
474
+ return this.processMultipleAnswersNode(node);
475
+ }
476
+ return null;
477
+ }
478
+ processMultipleChoiceNode(node) {
479
+ const specificAttrs = {
480
+ choices: `data-x-choices="${node.choices ?? ""}"`
481
+ };
482
+ return this.buildQuestionHtml(
483
+ node,
484
+ "x-multiple-choice",
485
+ specificAttrs
486
+ );
487
+ }
488
+ processMultipleAnswersNode(node) {
489
+ const specificAttrs = {
490
+ grading: node.grading ? `data-x-exact-match="${node.grading === "exactMatch" ? "true" : "false"}"` : "",
491
+ correctChoices: `data-x-correct-choices="${node.correctChoices ?? ""}"`,
492
+ incorrectChoices: `data-x-incorrect-choices="${node.incorrectChoices ?? ""}"`
493
+ };
494
+ return this.buildQuestionHtml(
495
+ node,
496
+ "x-multiple-answers",
497
+ specificAttrs
498
+ );
499
+ }
500
+ buildQuestionHtml(node, tagName, specificAttrs) {
501
+ const options = this.processOptions(node);
502
+ const commonAttrs = {
503
+ points: `data-x-points="${node.points}"`,
504
+ noneOfTheAbove: `data-x-none-of-the-above="${node.noneOfTheAbove ? "true" : "false"}"`
505
+ };
506
+ const allAttrs = { ...commonAttrs, ...specificAttrs };
507
+ const attributes = Object.values(allAttrs).filter(Boolean).join(" ");
508
+ return `<${tagName} id="${node.id}" data-x-prompt ${attributes}>${options.join("")}</${tagName}>`;
509
+ }
510
+ processOptions(node) {
511
+ const options = [];
512
+ for (const option of node.options) {
513
+ options.push(
514
+ `<x-option id="${option.id}" data-x-correct="${option.correct}">${createHtmlFromNestedEditor(
515
+ option.content,
516
+ {
517
+ parentNode: node
518
+ }
519
+ )}</x-option>`
520
+ );
521
+ }
522
+ return options;
523
+ }
524
+ };
525
+
526
+ // src/typeGuards/isSerializedShortAnswerQuestionNode.ts
527
+ function isSerializedShortAnswerQuestionNode(node) {
528
+ return node?.type === "short-answer-question" && "id" in node && typeof node.id === "string" && "points" in node && typeof node.points === "number" && "maxWords" in node && typeof node.maxWords === "number" && "notes" in node;
529
+ }
530
+
531
+ // src/typeGuards/isSerializedTableCellNode.ts
532
+ function isSerializedTableCellNode(node) {
533
+ return node?.type === "tablecell";
534
+ }
535
+
536
+ // src/exportToHtml/ParagraphNodeHandler.ts
537
+ var ELEMENT_TYPE_TO_FORMAT = {
538
+ center: "center",
539
+ end: "end",
540
+ justify: "justify",
541
+ left: "left",
542
+ right: "right",
543
+ start: "start"
544
+ };
545
+ var ParagraphNodeHandler = class extends NodeHandler {
546
+ shouldAvoidParagraphWrap(node) {
547
+ if (node === void 0) return false;
548
+ return isSerializedFillInTheBlankQuestionNode(node) || isSerializedFinancialStatementQuestionNode(node) || isSerializedJournalEntryQuestionNode(node) || isSerializedMatchingQuestionNode(node) || isSerializedMultipleOptionQuestionNode(node) || isSerializedShortAnswerQuestionNode(node) || isSerializedTableCellNode(node);
549
+ }
550
+ processNode(node, metadata) {
551
+ if (!isSerializedParagraphNode(node)) return null;
552
+ const children = [];
553
+ node.children.forEach((child) => {
554
+ const childString = traverse(child, {
555
+ ...metadata,
556
+ parentNode: node
557
+ });
558
+ if (childString) {
559
+ children.push(childString);
560
+ }
561
+ });
562
+ const attribute = node.format ? ` style="text-align: ${ELEMENT_TYPE_TO_FORMAT[node.format]}"` : "";
563
+ if (this.shouldAvoidParagraphWrap(metadata?.parentNode))
564
+ if (attribute)
565
+ return `<div${attribute}>${children.join("")}</div>`;
566
+ else return children.join("");
567
+ return `<p${attribute}>${children.join("")}</p>`;
568
+ }
569
+ };
570
+
571
+ // src/exportToHtml/ShortAnswerQuestionNodeHandler.ts
572
+ var ShortAnswerQuestionNodeHandler = class extends NodeHandler {
573
+ processNode(node, metadata) {
574
+ if (!isSerializedShortAnswerQuestionNode(node)) {
575
+ return null;
576
+ }
577
+ const shortAnswerNode = node;
578
+ const attributes = [
579
+ `id="${shortAnswerNode.id}"`,
580
+ `data-x-prompt`,
581
+ `data-x-points="${shortAnswerNode.points}"`,
582
+ `data-x-max-words="${shortAnswerNode.maxWords}"`
583
+ ];
584
+ const notesContent = createHtmlFromNestedEditor(
585
+ shortAnswerNode.notes,
586
+ {
587
+ ...metadata,
588
+ parentNode: shortAnswerNode
589
+ }
590
+ );
591
+ return `<x-short-answer ${attributes.join(" ")}><x-notes>${notesContent}</x-notes></x-short-answer>`;
592
+ }
593
+ };
594
+
595
+ // src/typeGuards/isSerializedSimulationQuestionNode.ts
596
+ function isSerializedSimulationQuestionNode(node) {
597
+ return node?.type === "simulation-question" && "id" in node && typeof node.id === "string" && "points" in node && typeof node.points === "number" && "step2Instructions" in node && typeof node.step2Instructions === "string";
598
+ }
599
+
600
+ // src/exportToHtml/SimulationQuestionNodeHandler.ts
601
+ var SimulationQuestionNodeHandler = class extends NodeHandler {
602
+ processNode(node) {
603
+ if (!isSerializedSimulationQuestionNode(node)) return null;
604
+ const aiChatModelAttr = node.aiChatModel ? ` data-x-ai-chat-model="${node.aiChatModel}"` : "";
605
+ const aiSystemMessage = node.aiSystemMessage ? `<x-ai-system-message>${node.aiSystemMessage}</x-ai-system-message>` : "";
606
+ const step2Instructions = node.step2Instructions ? `<x-step2-instructions>${node.step2Instructions}</x-step2-instructions>` : "";
607
+ return `<x-simulation id="${node.id}" data-x-prompt data-x-points="${node.points}"${aiChatModelAttr}>${aiSystemMessage}${step2Instructions}</x-simulation>`;
608
+ }
609
+ };
610
+
611
+ // src/typeGuards/isSerializedTableRowNode.ts
612
+ function isSerializedTableRowNode(node) {
613
+ return node?.type === "tablerow" && "children" in node && Array.isArray(node.children);
614
+ }
615
+
616
+ // src/exportToHtml/TableCellNodeHandler.ts
617
+ var TableCellNodeHandler = class extends NodeHandler {
618
+ processNode(node, metadata) {
619
+ if (!isSerializedTableCellNode(node)) return null;
620
+ const tableCellNode = node;
621
+ const styles = [];
622
+ let cellIndex = -1;
623
+ if (metadata?.parentNode && isSerializedTableRowNode(metadata.parentNode)) {
624
+ const rowNode = metadata.parentNode;
625
+ for (let i = 0; i < rowNode.children.length; i++) {
626
+ if (rowNode.children[i] === node) {
627
+ cellIndex = i;
628
+ break;
629
+ }
630
+ }
631
+ }
632
+ if (metadata?.colWidthPixels && cellIndex >= 0 && cellIndex < metadata.colWidthPixels.length) {
633
+ styles.push(`width: ${metadata.colWidthPixels[cellIndex]}px;`);
634
+ } else if (tableCellNode.width) {
635
+ styles.push(`width: ${tableCellNode.width}px;`);
636
+ }
637
+ if (tableCellNode.backgroundColor) {
638
+ styles.push(
639
+ `background-color: ${tableCellNode.backgroundColor};`
640
+ );
641
+ }
642
+ if (tableCellNode.verticalAlign) {
643
+ styles.push(`vertical-align: ${tableCellNode.verticalAlign};`);
644
+ }
645
+ const styleAttr = styles.length > 0 ? ` style="${styles.join(" ")}"` : "";
646
+ const tag = tableCellNode.headerState > 0 ? "th" : "td";
647
+ return `<${tag}${styleAttr}>${createHtmlFromNestedNodes(tableCellNode.children, { parentNode: tableCellNode })}</${tag}>`;
648
+ }
649
+ };
650
+
651
+ // src/typeGuards/isSerializedTableNode.ts
652
+ function isSerializedTableNode(node) {
653
+ return node?.type === "table" && "children" in node && Array.isArray(node.children);
654
+ }
655
+
656
+ // src/exportToHtml/TableNodeHandler.ts
657
+ var TableNodeHandler = class extends NodeHandler {
658
+ processNode(node, metadata) {
659
+ if (!isSerializedTableNode(node)) return null;
660
+ const tableNode = node;
661
+ const tableWidth = tableNode.colWidths?.reduce(
662
+ (acc, width) => acc + width,
663
+ 0
664
+ );
665
+ let tableWidthStyle = "";
666
+ if (tableWidth) {
667
+ tableWidthStyle = `width: ${tableWidth}px;`;
668
+ }
669
+ const tableStyles = tableWidthStyle ? [tableWidthStyle] : [];
670
+ if (tableNode.rowStriping) {
671
+ tableStyles.push("border-collapse: collapse;");
672
+ }
673
+ const tableStyleAttr = tableStyles.length > 0 ? ` style="${tableStyles.join(" ")}"` : "";
674
+ const headerRows = [];
675
+ const bodyRows = [];
676
+ const isHeaderRow = (rowString) => {
677
+ return rowString.includes("<th");
678
+ };
679
+ const colWidths = [];
680
+ if (tableNode.colWidths) {
681
+ colWidths.push(...tableNode.colWidths);
682
+ }
683
+ tableNode.children?.forEach(
684
+ (child, index) => {
685
+ const childString = traverse(child, {
686
+ ...metadata,
687
+ parentNode: tableNode,
688
+ colWidthPixels: colWidths
689
+ });
690
+ if (childString) {
691
+ if (index < 2 && isHeaderRow(childString)) {
692
+ headerRows.push(childString);
693
+ } else {
694
+ bodyRows.push(childString);
695
+ }
696
+ }
697
+ }
698
+ );
699
+ let tableHtml = `<table${tableStyleAttr}>`;
700
+ if (headerRows.length > 0) {
701
+ tableHtml += `<thead>${headerRows.join("")}</thead>`;
702
+ }
703
+ if (bodyRows.length > 0) {
704
+ tableHtml += `<tbody>${bodyRows.join("")}</tbody>`;
705
+ }
706
+ tableHtml += "</table>";
707
+ return tableHtml;
708
+ }
709
+ };
710
+
711
+ // src/exportToHtml/TableRowNodeHandler.ts
712
+ var TableRowNodeHandler = class extends NodeHandler {
713
+ processNode(node, metadata) {
714
+ if (!isSerializedTableRowNode(node)) return null;
715
+ const tableRowNode = node;
716
+ const children = [];
717
+ tableRowNode.children?.forEach((child) => {
718
+ const childString = traverse(child, {
719
+ parentNode: node,
720
+ colWidthPixels: metadata?.colWidthPixels
721
+ });
722
+ if (childString) {
723
+ children.push(childString);
724
+ }
725
+ });
726
+ const heightAttr = tableRowNode.height ? ` style="height: ${tableRowNode.height}px;"` : "";
727
+ return `<tr${heightAttr}>${children.join("")}</tr>`;
728
+ }
729
+ };
730
+
731
+ // src/typeGuards/isSerializedTextNode.ts
732
+ function isSerializedTextNode(node) {
733
+ return node?.type === "text" && "text" in node && typeof node.text === "string";
734
+ }
735
+
736
+ // src/exportToHtml/TextNodeHandler.ts
737
+ var IS_BOLD = 1;
738
+ var IS_ITALIC = 1 << 1;
739
+ var IS_STRIKETHROUGH = 1 << 2;
740
+ var IS_UNDERLINE = 1 << 3;
741
+ var IS_CODE = 1 << 4;
742
+ var IS_SUBSCRIPT = 1 << 5;
743
+ var IS_SUPERSCRIPT = 1 << 6;
744
+ var IS_HIGHLIGHT = 1 << 7;
745
+ var IS_LOWERCASE = 1 << 8;
746
+ var IS_UPPERCASE = 1 << 9;
747
+ var IS_CAPITALIZE = 1 << 10;
748
+ var TEXT_TYPE_TO_FORMAT = {
749
+ bold: IS_BOLD,
750
+ capitalize: IS_CAPITALIZE,
751
+ code: IS_CODE,
752
+ highlight: IS_HIGHLIGHT,
753
+ italic: IS_ITALIC,
754
+ lowercase: IS_LOWERCASE,
755
+ strikethrough: IS_STRIKETHROUGH,
756
+ subscript: IS_SUBSCRIPT,
757
+ superscript: IS_SUPERSCRIPT,
758
+ underline: IS_UNDERLINE,
759
+ uppercase: IS_UPPERCASE
760
+ };
761
+ function hasFormat(format, type) {
762
+ const formatFlag = TEXT_TYPE_TO_FORMAT[type];
763
+ return (format & formatFlag) !== 0;
764
+ }
765
+ function encodeHTMLEntities(text) {
766
+ return text.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
767
+ }
768
+ var TextNodeHandler = class extends NodeHandler {
769
+ processNode(node) {
770
+ if (!isSerializedTextNode(node)) return null;
771
+ let result = encodeHTMLEntities(node.text);
772
+ if (hasFormat(node.format, "subscript")) {
773
+ result = `<sub>${result}</sub>`;
774
+ } else if (hasFormat(node.format, "superscript")) {
775
+ result = `<sup>${result}</sup>`;
776
+ }
777
+ if (hasFormat(node.format, "code")) {
778
+ result = `<code>${result}</code>`;
779
+ }
780
+ if (hasFormat(node.format, "strikethrough")) {
781
+ result = `<s>${result}</s>`;
782
+ }
783
+ if (hasFormat(node.format, "underline")) {
784
+ result = `<u>${result}</u>`;
785
+ }
786
+ if (hasFormat(node.format, "italic")) {
787
+ result = `<em>${result}</em>`;
788
+ }
789
+ if (hasFormat(node.format, "bold")) {
790
+ result = `<strong>${result}</strong>`;
791
+ }
792
+ const styles = [];
793
+ if (hasFormat(node.format, "lowercase")) {
794
+ styles.push("text-transform: lowercase");
795
+ } else if (hasFormat(node.format, "uppercase")) {
796
+ styles.push("text-transform: uppercase");
797
+ } else if (hasFormat(node.format, "capitalize")) {
798
+ styles.push("text-transform: capitalize");
799
+ }
800
+ if (hasFormat(node.format, "highlight")) {
801
+ styles.push("background-color: yellow");
802
+ }
803
+ let styleString = styles.join(";");
804
+ if (node.style) {
805
+ styleString = styleString ? `${styleString};${node.style}` : node.style;
806
+ }
807
+ if (styleString.trim().length > 0) {
808
+ result = `<span style="${styleString.trim()}">${result}</span>`;
809
+ }
810
+ return result;
811
+ }
812
+ };
813
+
814
+ // src/typeGuards/isSerializedVariableNode.ts
815
+ function isSerializedVariableNode(node) {
816
+ return node?.type === "variable" && typeof node.variableName === "string";
817
+ }
818
+
819
+ // src/exportToHtml/VariableNodeHandler.ts
820
+ var VariableNodeHandler = class extends NodeHandler {
821
+ processNode(node) {
822
+ if (!isSerializedVariableNode(node)) return null;
823
+ const formatAttribute = node.variableFormat ? ` data-x-format="${node.variableFormat}"` : "";
824
+ let result = `<x-param data-x-name="${node.variableName}"${formatAttribute}></x-param>`;
825
+ if (node.isUnderline) {
826
+ result = `<u>${result}</u>`;
827
+ }
828
+ if (node.isItalic) {
829
+ result = `<em>${result}</em>`;
830
+ }
831
+ if (node.isBold) {
832
+ result = `<strong>${result}</strong>`;
833
+ }
834
+ return result;
835
+ }
836
+ };
837
+
838
+ // src/exportToHtml/traverse.ts
839
+ var builder = new NodeHandlerChainBuilder();
840
+ var nodeHandlerChain = builder.addHandler(EssayQuestionNodeHandler).addHandler(FillInTheBlankQuestionNodeHandler).addHandler(FillInTheBlankSpaceNodeHandler).addHandler(FinancialStatementQuestionNodeHandler).addHandler(HorizontalRuleNodeHandler).addHandler(ImageNodeHandler).addHandler(JournalEntryQuestionNodeHandler).addHandler(LineBreakNodeHandler).addHandler(LinkNodeHandler).addHandler(ListNodeHandler).addHandler(ListItemNodeHandler).addHandler(MatchingQuestionNodeHandler).addHandler(MultipleOptionQuestionNodeHandler).addHandler(ParagraphNodeHandler).addHandler(ShortAnswerQuestionNodeHandler).addHandler(SimulationQuestionNodeHandler).addHandler(TableCellNodeHandler).addHandler(TableNodeHandler).addHandler(TableRowNodeHandler).addHandler(TextNodeHandler).addHandler(VariableNodeHandler).build();
841
+ var traverse = (node, metadata) => {
842
+ return nodeHandlerChain.handle(node, metadata);
843
+ };
844
+
845
+ // src/exportToHtml/index.ts
846
+ function exportToHtml(editorState) {
847
+ const rootNode = editorState.root;
848
+ const children = [];
849
+ for (const childNode of rootNode.children) {
850
+ const element = traverse(childNode);
851
+ if (element) {
852
+ children.push(element);
853
+ }
854
+ }
855
+ return children.join("");
856
+ }
857
+
858
+ // src/importFromHtml/index.ts
859
+ var import_node_html_parser26 = require("node-html-parser");
860
+
861
+ // src/importFromHtml/createEmptyParagraphNode.ts
862
+ function createEmptyParagraphNode(format = "") {
863
+ return {
864
+ direction: "ltr",
865
+ format,
866
+ indent: 0,
867
+ textFormat: 0,
868
+ textStyle: "",
869
+ type: "paragraph",
870
+ version: 1,
871
+ children: []
872
+ };
873
+ }
874
+
875
+ // src/importFromHtml/DivNodeHandler.ts
876
+ var import_node_html_parser2 = require("node-html-parser");
877
+
878
+ // src/importFromHtml/createNestedNodesFromHtml.ts
879
+ var import_node_html_parser = require("node-html-parser");
880
+ var wrapInlinesInParagraph = (inlineNodes, format = "") => {
881
+ const paragraph = createEmptyParagraphNode(format);
882
+ paragraph.children = inlineNodes;
883
+ return paragraph;
884
+ };
885
+ var appendInlinesInParagraphIfNotEmptyBR = (results, inlineNodes, format) => {
886
+ if (!inlineNodes.length) return;
887
+ if (inlineNodes.length === 1 && isSerializedLineBreakNode(inlineNodes[0]))
888
+ return;
889
+ results.push(wrapInlinesInParagraph(inlineNodes, format));
890
+ };
891
+ var extractFormatFromStyle = (styleAttribute) => {
892
+ if (!styleAttribute) return "";
893
+ const textAlignMatch = styleAttribute.match(
894
+ /text-align:\s*(left|right|center)/i
895
+ );
896
+ if (textAlignMatch) {
897
+ const alignment = textAlignMatch[1].toLowerCase();
898
+ if (alignment === "left") return "left";
899
+ if (alignment === "right") return "right";
900
+ if (alignment === "center") return "center";
901
+ }
902
+ return "";
903
+ };
904
+ var createNestedNodesFromHtml = (node) => {
905
+ const parentStyleAttribute = node instanceof import_node_html_parser.HTMLElement ? node.getAttribute("style") : null;
906
+ const format = extractFormatFromStyle(parentStyleAttribute);
907
+ if (node.childNodes.length === 1 && node.childNodes[0] instanceof import_node_html_parser.HTMLElement && node.childNodes[0].tagName === "BR") {
908
+ return [createEmptyParagraphNode(format)];
909
+ }
910
+ const results = [];
911
+ let inlineNodes = [];
912
+ for (const child of node.childNodes) {
913
+ const processedChildren = traverse2(child);
914
+ for (const processedChild of processedChildren) {
915
+ const isBlockElement = processedChild.type === "paragraph" || processedChild.type === "table" || processedChild.type === "list";
916
+ if (isBlockElement) {
917
+ appendInlinesInParagraphIfNotEmptyBR(
918
+ results,
919
+ inlineNodes,
920
+ format
921
+ );
922
+ inlineNodes = [];
923
+ results.push(processedChild);
924
+ } else {
925
+ inlineNodes.push(processedChild);
926
+ }
927
+ }
928
+ }
929
+ appendInlinesInParagraphIfNotEmptyBR(results, inlineNodes, format);
930
+ if (results.length === 0) return [createEmptyParagraphNode(format)];
931
+ else return results;
932
+ };
933
+
934
+ // src/importFromHtml/types.ts
935
+ var NodeHandler2 = class {
936
+ constructor() {
937
+ this.nextHandler = null;
938
+ }
939
+ getChildNodesTextContent(childNodes) {
940
+ return childNodes.reduce((acc, item) => {
941
+ const text = item.textContent || "";
942
+ return acc + text.trim() + " ";
943
+ }, "").trimEnd();
944
+ }
945
+ setNext(handler) {
946
+ this.nextHandler = handler;
947
+ return handler;
948
+ }
949
+ handle(node) {
950
+ const result = this.processNode(node);
951
+ if (result) {
952
+ return result;
953
+ } else if (this.nextHandler) {
954
+ return this.nextHandler.handle(node);
955
+ }
956
+ return null;
957
+ }
958
+ };
959
+
960
+ // src/importFromHtml/DivNodeHandler.ts
961
+ var isHtmlDivElement = (node) => {
962
+ return node instanceof import_node_html_parser2.HTMLElement && node.tagName === "DIV";
963
+ };
964
+ var DivNodeHandler = class extends NodeHandler2 {
965
+ processNode(node) {
966
+ if (!isHtmlDivElement(node)) {
967
+ return null;
968
+ }
969
+ return createNestedNodesFromHtml(node);
970
+ }
971
+ };
972
+
973
+ // src/importFromHtml/EssayQuestionNodeHandler.ts
974
+ var import_nanoid = require("nanoid");
975
+ var import_node_html_parser3 = require("node-html-parser");
976
+ var TAG_X_ESSAY = "x-essay";
977
+ var TAG_X_SYSTEM_MESSAGE = "x-ai-system-message";
978
+ var EssayQuestionNodeHandler2 = class extends NodeHandler2 {
979
+ processNode(node) {
980
+ if (!(node instanceof import_node_html_parser3.HTMLElement) || node.tagName !== TAG_X_ESSAY.toUpperCase()) {
981
+ return null;
982
+ }
983
+ const jsonNode = {
984
+ id: node.getAttribute("id") ?? (0, import_nanoid.nanoid)(),
985
+ points: Number(node.getAttribute("data-x-points") || 0),
986
+ maxWords: Number(node.getAttribute("data-x-max-words") || 0),
987
+ hideChat: node.getAttribute("data-x-hide-chat") === "true",
988
+ aiChatModel: node.getAttribute("data-x-ai-chat-model") || null,
989
+ aiSystemMessage: "",
990
+ type: "essay-question",
991
+ version: 1
992
+ };
993
+ const systemMessageElement = node.querySelector(
994
+ TAG_X_SYSTEM_MESSAGE
995
+ );
996
+ if (systemMessageElement)
997
+ jsonNode.aiSystemMessage = this.getChildNodesTextContent(
998
+ systemMessageElement.childNodes
999
+ );
1000
+ return jsonNode;
1001
+ }
1002
+ };
1003
+
1004
+ // src/importFromHtml/FillInTheBlankQuestionNodeHandler.ts
1005
+ var import_nanoid2 = require("nanoid");
1006
+ var import_node_html_parser4 = require("node-html-parser");
1007
+
1008
+ // src/importFromHtml/createNestedEditorFromHtml.ts
1009
+ function createNestedEditorFromHtml(node) {
1010
+ return {
1011
+ editorState: {
1012
+ root: {
1013
+ direction: "ltr",
1014
+ format: "",
1015
+ indent: 0,
1016
+ version: 0,
1017
+ type: "root",
1018
+ children: node ? createNestedNodesFromHtml(node) : [createEmptyParagraphNode()]
1019
+ }
1020
+ }
1021
+ };
1022
+ }
1023
+
1024
+ // src/importFromHtml/FillInTheBlankQuestionNodeHandler.ts
1025
+ var FillInTheBlankQuestionNodeHandler2 = class extends NodeHandler2 {
1026
+ processNode(node) {
1027
+ if (!(node instanceof import_node_html_parser4.HTMLElement) || node.tagName !== "x-fill-in-the-blank".toUpperCase())
1028
+ return null;
1029
+ const jsonNode = {
1030
+ id: node.getAttribute("id") ?? (0, import_nanoid2.nanoid)(),
1031
+ pointsPerSpace: Number(
1032
+ node.getAttribute("data-x-points-per-space") ?? 1
1033
+ ),
1034
+ content: createNestedEditorFromHtml(node),
1035
+ type: "fill-in-the-blank-question",
1036
+ version: 1
1037
+ };
1038
+ return jsonNode;
1039
+ }
1040
+ };
1041
+
1042
+ // src/importFromHtml/FillInTheBlankSpaceNodeHandler.ts
1043
+ var import_nanoid3 = require("nanoid");
1044
+ var import_node_html_parser5 = require("node-html-parser");
1045
+ var FillInTheBlankSpaceNodeHandler2 = class extends NodeHandler2 {
1046
+ processNode(node) {
1047
+ if (!(node instanceof import_node_html_parser5.HTMLElement) || node.tagName !== "x-space".toUpperCase())
1048
+ return null;
1049
+ const spaceTypeAttr = node.getAttribute("data-x-type");
1050
+ const spaceType = spaceTypeAttr === "Text" || spaceTypeAttr === "Number" || spaceTypeAttr === "Dropdown" ? spaceTypeAttr : "Text";
1051
+ const baseNode = {
1052
+ id: node.getAttribute("id") ?? (0, import_nanoid3.nanoid)(),
1053
+ spaceName: node.text,
1054
+ // Can probably be removed after this: https://github.com/examind-ai/block-editor/issues/137
1055
+ matches: node.getAttribute("data-x-matches") ?? "",
1056
+ spaceType,
1057
+ type: "space",
1058
+ version: 1
1059
+ };
1060
+ switch (spaceType) {
1061
+ case "Text":
1062
+ return this.processTextSpace(node, baseNode);
1063
+ case "Number":
1064
+ return this.processNumberSpace(node, baseNode);
1065
+ case "Dropdown":
1066
+ return this.processDropdownSpace(node, baseNode);
1067
+ default:
1068
+ throw new Error(`Unsupported space type: ${spaceType}`);
1069
+ }
1070
+ }
1071
+ processTextSpace(node, baseNode) {
1072
+ return {
1073
+ ...baseNode,
1074
+ caseSensitive: node.getAttribute("data-x-case-sensitive") === "true",
1075
+ errorsAllowed: node.getAttribute("data-x-errors-allowed") ? Number(node.getAttribute("data-x-errors-allowed")) : void 0
1076
+ };
1077
+ }
1078
+ processNumberSpace(node, baseNode) {
1079
+ return {
1080
+ ...baseNode,
1081
+ minDecimals: node.getAttribute("data-x-min-decimals") ? Number(node.getAttribute("data-x-min-decimals")) : void 0,
1082
+ errorTolerance: node.getAttribute("data-x-error-tolerance") ? Number(node.getAttribute("data-x-error-tolerance")) : void 0
1083
+ };
1084
+ }
1085
+ processDropdownSpace(node, baseNode) {
1086
+ return {
1087
+ ...baseNode,
1088
+ distractors: node.getAttribute("data-x-distractors") ?? void 0
1089
+ };
1090
+ }
1091
+ };
1092
+
1093
+ // src/importFromHtml/FinancialStatementQuestionNodeHandler.ts
1094
+ var import_nanoid4 = require("nanoid");
1095
+ var import_node_html_parser6 = require("node-html-parser");
1096
+ var TAG_X_FINANCIAL_STATEMENT = "x-financial-statement";
1097
+ var TAG_X_HEADER = "x-header";
1098
+ var TAG_X_ROWS = "x-rows";
1099
+ var TAG_X_DISTRACTORS = "x-distractors";
1100
+ var TAG_X_HEADING = "x-heading";
1101
+ var TAG_X_LINE = "x-line";
1102
+ var TAG_X_DISTRACTOR = "x-distractor";
1103
+ var TAG_X_ENTRY = "x-entry";
1104
+ var TAG_X_AMOUNT = "x-amount";
1105
+ var FinancialStatementQuestionNodeHandler2 = class extends NodeHandler2 {
1106
+ processNode(node) {
1107
+ if (!(node instanceof import_node_html_parser6.HTMLElement) || node.tagName !== TAG_X_FINANCIAL_STATEMENT.toUpperCase()) {
1108
+ return null;
1109
+ }
1110
+ const headerElement = node.querySelector(TAG_X_HEADER);
1111
+ const jsonNode = {
1112
+ id: node.getAttribute("id") ?? (0, import_nanoid4.nanoid)(),
1113
+ points: Number(node.getAttribute("data-x-points") || 10),
1114
+ header: this.processHeaderElement(headerElement),
1115
+ columnHeaders: this.extractColumnHeaders(headerElement),
1116
+ rows: [],
1117
+ distractors: [],
1118
+ type: "financial-statement-question",
1119
+ version: 1
1120
+ };
1121
+ node.childNodes.forEach((child) => {
1122
+ if (!(child instanceof import_node_html_parser6.HTMLElement)) return;
1123
+ if (child.tagName === TAG_X_DISTRACTORS.toUpperCase()) {
1124
+ this.processDistractorsNode(child, jsonNode);
1125
+ } else if (child.tagName === TAG_X_ROWS.toUpperCase()) {
1126
+ this.processRowsNode(child, jsonNode);
1127
+ }
1128
+ });
1129
+ return jsonNode;
1130
+ }
1131
+ processDistractorsNode(distractorsNode, jsonNode) {
1132
+ for (const distractorsChild of distractorsNode.childNodes) {
1133
+ if (!(distractorsChild instanceof import_node_html_parser6.HTMLElement)) continue;
1134
+ if (distractorsChild.tagName !== TAG_X_DISTRACTOR.toUpperCase())
1135
+ continue;
1136
+ jsonNode.distractors.push({
1137
+ id: distractorsChild.getAttribute("id") || (0, import_nanoid4.nanoid)(),
1138
+ entry: this.getChildNodesTextContent(
1139
+ distractorsChild.childNodes
1140
+ )
1141
+ });
1142
+ }
1143
+ }
1144
+ // Using the getChildNodesTextContent method from the base NodeHandler class
1145
+ processHeaderElement(headerElement) {
1146
+ if (!headerElement) return createNestedEditorFromHtml(null);
1147
+ const flexDiv = headerElement.querySelector(
1148
+ 'div[style*="display:flex"]'
1149
+ );
1150
+ if (flexDiv) {
1151
+ const contentDiv = headerElement.querySelector(
1152
+ 'div > div:not([style*="display:flex"])'
1153
+ );
1154
+ if (contentDiv && contentDiv instanceof import_node_html_parser6.HTMLElement) {
1155
+ const contentDivClone = contentDiv.clone();
1156
+ contentDivClone.setAttribute("style", "text-align: left;");
1157
+ return createNestedEditorFromHtml(contentDivClone);
1158
+ }
1159
+ }
1160
+ return createNestedEditorFromHtml(headerElement);
1161
+ }
1162
+ extractColumnHeaders(headerElement) {
1163
+ if (!headerElement) return [];
1164
+ const flexDiv = headerElement.querySelector(
1165
+ 'div[style*="display:flex"]'
1166
+ );
1167
+ if (flexDiv) {
1168
+ return Array.from(flexDiv.querySelectorAll("div")).map((div) => div.textContent?.trim() || "").filter(Boolean);
1169
+ }
1170
+ return [];
1171
+ }
1172
+ processRowsNode(rowsNode, jsonNode) {
1173
+ for (const rowsChild of rowsNode.childNodes) {
1174
+ if (!(rowsChild instanceof import_node_html_parser6.HTMLElement)) continue;
1175
+ if (rowsChild.tagName === TAG_X_HEADING.toUpperCase()) {
1176
+ this.processHeadingRow(rowsChild, jsonNode);
1177
+ } else if (rowsChild.tagName === TAG_X_LINE.toUpperCase()) {
1178
+ this.processLineRow(rowsChild, jsonNode);
1179
+ }
1180
+ }
1181
+ }
1182
+ processHeadingRow(headingNode, jsonNode) {
1183
+ jsonNode.rows.push({
1184
+ id: headingNode.getAttribute("id") || (0, import_nanoid4.nanoid)(),
1185
+ depth: Number(headingNode.getAttribute("data-x-depth") || 0),
1186
+ isHint: headingNode.getAttribute("data-x-is-hint") === "true",
1187
+ type: "Heading",
1188
+ entry: headingNode.rawText
1189
+ });
1190
+ }
1191
+ processLineRow(lineNode, jsonNode) {
1192
+ jsonNode.rows.push({
1193
+ type: "Line",
1194
+ role: "line-item",
1195
+ id: lineNode.getAttribute("id") || "",
1196
+ depth: Number(lineNode.getAttribute("data-x-depth") || 0),
1197
+ entry: lineNode.querySelector(TAG_X_ENTRY)?.textContent || "",
1198
+ amount: createNestedEditorFromHtml(
1199
+ lineNode.querySelector(TAG_X_AMOUNT)
1200
+ ),
1201
+ isAmountHint: lineNode.getAttribute("data-x-is-amount-hint") === "true",
1202
+ isEntryHint: lineNode.getAttribute("data-x-is-entry-hint") === "true"
1203
+ });
1204
+ }
1205
+ };
1206
+
1207
+ // src/importFromHtml/FormattedNodeHandler.ts
1208
+ var import_node_html_parser7 = require("node-html-parser");
1209
+ var IS_BOLD2 = 1;
1210
+ var IS_ITALIC2 = 1 << 1;
1211
+ var IS_STRIKETHROUGH2 = 1 << 2;
1212
+ var IS_UNDERLINE2 = 1 << 3;
1213
+ var IS_CODE2 = 1 << 4;
1214
+ var IS_SUBSCRIPT2 = 1 << 5;
1215
+ var IS_SUPERSCRIPT2 = 1 << 6;
1216
+ var IS_HIGHLIGHT2 = 1 << 7;
1217
+ var IS_LOWERCASE2 = 1 << 8;
1218
+ var IS_UPPERCASE2 = 1 << 9;
1219
+ var IS_CAPITALIZE2 = 1 << 10;
1220
+ var TAG_TO_FORMAT = {
1221
+ B: IS_BOLD2,
1222
+ STRONG: IS_BOLD2,
1223
+ I: IS_ITALIC2,
1224
+ EM: IS_ITALIC2,
1225
+ U: IS_UNDERLINE2,
1226
+ S: IS_STRIKETHROUGH2,
1227
+ STRIKE: IS_STRIKETHROUGH2,
1228
+ DEL: IS_STRIKETHROUGH2,
1229
+ SUB: IS_SUBSCRIPT2,
1230
+ SUP: IS_SUPERSCRIPT2,
1231
+ CODE: IS_CODE2,
1232
+ MARK: IS_HIGHLIGHT2
1233
+ };
1234
+ function formatToProps(format) {
1235
+ return {
1236
+ isBold: (format & IS_BOLD2) !== 0,
1237
+ isItalic: (format & IS_ITALIC2) !== 0,
1238
+ isStrikethrough: (format & IS_STRIKETHROUGH2) !== 0,
1239
+ isUnderline: (format & IS_UNDERLINE2) !== 0,
1240
+ isCode: (format & IS_CODE2) !== 0,
1241
+ isSubscript: (format & IS_SUBSCRIPT2) !== 0,
1242
+ isSuperscript: (format & IS_SUPERSCRIPT2) !== 0,
1243
+ isHighlight: (format & IS_HIGHLIGHT2) !== 0
1244
+ };
1245
+ }
1246
+ var FormattedNodeHandler = class extends NodeHandler2 {
1247
+ processNode(node) {
1248
+ if (!(node instanceof import_node_html_parser7.HTMLElement)) return null;
1249
+ const tagName = node.tagName;
1250
+ if (!tagName || !(tagName in TAG_TO_FORMAT)) return null;
1251
+ const formatFlag = TAG_TO_FORMAT[tagName];
1252
+ const results = [];
1253
+ for (const childNode of node.childNodes) {
1254
+ const processedChildren = traverse2(childNode);
1255
+ for (const child of processedChildren) {
1256
+ if (isSerializedTextNode(child)) {
1257
+ const result = {
1258
+ ...child,
1259
+ format: (child.format || 0) | formatFlag
1260
+ };
1261
+ results.push(result);
1262
+ } else if (isSerializedVariableNode(child)) {
1263
+ const result = {
1264
+ ...child,
1265
+ isBold: child.isBold || formatToProps(formatFlag).isBold,
1266
+ isItalic: child.isItalic || formatToProps(formatFlag).isItalic,
1267
+ isUnderline: child.isUnderline || formatToProps(formatFlag).isUnderline
1268
+ };
1269
+ results.push(result);
1270
+ } else {
1271
+ results.push(child);
1272
+ }
1273
+ }
1274
+ }
1275
+ if (results.length > 0) {
1276
+ return results;
1277
+ }
1278
+ return null;
1279
+ }
1280
+ };
1281
+
1282
+ // src/importFromHtml/HorizontalRuleNodeHandler.ts
1283
+ var import_node_html_parser8 = require("node-html-parser");
1284
+ var TAG_HR = "hr";
1285
+ var HorizontalRuleNodeHandler2 = class extends NodeHandler2 {
1286
+ processNode(node) {
1287
+ if (!(node instanceof import_node_html_parser8.HTMLElement) || node.tagName !== TAG_HR.toUpperCase()) {
1288
+ return null;
1289
+ }
1290
+ const horizontalRuleNode = {
1291
+ type: "horizontalrule",
1292
+ version: 1
1293
+ };
1294
+ return horizontalRuleNode;
1295
+ }
1296
+ };
1297
+
1298
+ // src/importFromHtml/ImageNodeHandler.ts
1299
+ var import_node_html_parser9 = require("node-html-parser");
1300
+ var TAG_IMG = "img";
1301
+ var ImageNodeHandler2 = class extends NodeHandler2 {
1302
+ processNode(node) {
1303
+ if (!(node instanceof import_node_html_parser9.HTMLElement) || node.tagName !== TAG_IMG.toUpperCase()) {
1304
+ return null;
1305
+ }
1306
+ const src = node.getAttribute("src") || "";
1307
+ const altText = node.getAttribute("alt") || "";
1308
+ const width = node.getAttribute("width") || "";
1309
+ const align = node.getAttribute("align") || "left";
1310
+ const imageNode = {
1311
+ src,
1312
+ altText,
1313
+ width,
1314
+ align,
1315
+ showBorder: false,
1316
+ showCaption: false,
1317
+ caption: {
1318
+ editorState: {
1319
+ root: {
1320
+ children: [],
1321
+ direction: null,
1322
+ format: "",
1323
+ indent: 0,
1324
+ type: "root",
1325
+ version: 1
1326
+ }
1327
+ }
1328
+ },
1329
+ type: "image",
1330
+ version: 1
1331
+ };
1332
+ return imageNode;
1333
+ }
1334
+ };
1335
+
1336
+ // src/importFromHtml/JournalEntryQuestionNodeHandler.ts
1337
+ var import_nanoid5 = require("nanoid");
1338
+ var import_node_html_parser10 = require("node-html-parser");
1339
+ var JournalEntryQuestionNodeHandler2 = class extends NodeHandler2 {
1340
+ processNode(node) {
1341
+ if (!(node instanceof import_node_html_parser10.HTMLElement) || node.tagName !== "x-journal-entry".toUpperCase())
1342
+ return null;
1343
+ const noEntryRequiredCorrect = node.getAttribute("data-x-no-entry-required-correct") === "true";
1344
+ const noEntryRequiredDistractor = node.getAttribute("data-x-no-entry-required-distractor") === "true";
1345
+ const journalType = noEntryRequiredCorrect ? "noEntryRequiredCorrect" : noEntryRequiredDistractor ? "noEntryRequiredDistractor" : "default";
1346
+ const jsonNode = {
1347
+ id: node.getAttribute("id") ?? (0, import_nanoid5.nanoid)(),
1348
+ points: Number(node.getAttribute("data-x-points") ?? 1),
1349
+ journalType,
1350
+ errorTolerance: node.getAttribute("data-x-error-tolerance") ? Number(node.getAttribute("data-x-error-tolerance")) : void 0,
1351
+ lineItems: [],
1352
+ type: "journal-entry-question",
1353
+ version: 1
1354
+ };
1355
+ const lineItems = node.querySelectorAll("x-line-item");
1356
+ lineItems.forEach((lineItem) => {
1357
+ if (!(lineItem instanceof import_node_html_parser10.HTMLElement)) return;
1358
+ const accountElement = lineItem.querySelector("x-account");
1359
+ const debitElement = lineItem.querySelector("x-debit");
1360
+ const creditElement = lineItem.querySelector("x-credit");
1361
+ if (!accountElement) return;
1362
+ const item = {
1363
+ id: lineItem.getAttribute("id") ?? (0, import_nanoid5.nanoid)(),
1364
+ correct: true,
1365
+ account: createNestedEditorFromHtml(accountElement)
1366
+ };
1367
+ if (debitElement) {
1368
+ item.debit = createNestedEditorFromHtml(debitElement);
1369
+ }
1370
+ if (creditElement) {
1371
+ item.credit = createNestedEditorFromHtml(creditElement);
1372
+ }
1373
+ jsonNode.lineItems.push(item);
1374
+ });
1375
+ const distractors = node.querySelectorAll("x-distractor");
1376
+ distractors.forEach((distractor) => {
1377
+ if (!(distractor instanceof import_node_html_parser10.HTMLElement)) return;
1378
+ const item = {
1379
+ id: distractor.getAttribute("id") ?? (0, import_nanoid5.nanoid)(),
1380
+ correct: false,
1381
+ account: createNestedEditorFromHtml(distractor)
1382
+ };
1383
+ jsonNode.lineItems.push(item);
1384
+ });
1385
+ return jsonNode;
1386
+ }
1387
+ };
1388
+
1389
+ // src/importFromHtml/LineBreakNodeHandler.ts
1390
+ var import_node_html_parser11 = require("node-html-parser");
1391
+ var LineBreakNodeHandler2 = class extends NodeHandler2 {
1392
+ processNode(node) {
1393
+ if (!(node instanceof import_node_html_parser11.HTMLElement)) return null;
1394
+ if (node.tagName !== "BR") return null;
1395
+ return {
1396
+ type: "linebreak",
1397
+ version: 1
1398
+ };
1399
+ }
1400
+ };
1401
+
1402
+ // src/importFromHtml/LinkNodeHandler.ts
1403
+ var import_node_html_parser12 = require("node-html-parser");
1404
+ var TAG_A = "a";
1405
+ var LinkNodeHandler2 = class extends NodeHandler2 {
1406
+ processNode(node) {
1407
+ if (!(node instanceof import_node_html_parser12.HTMLElement) || node.tagName !== TAG_A.toUpperCase()) {
1408
+ return null;
1409
+ }
1410
+ const url = node.getAttribute("href") || "";
1411
+ const rel = node.getAttribute("rel") || null;
1412
+ const target = node.getAttribute("target") || null;
1413
+ const title = node.getAttribute("title") || null;
1414
+ const children = [];
1415
+ for (const childNode of node.childNodes) {
1416
+ const processedChildren = traverse2(childNode);
1417
+ children.push(...processedChildren);
1418
+ }
1419
+ const linkNode = {
1420
+ url,
1421
+ rel,
1422
+ target,
1423
+ title,
1424
+ children,
1425
+ direction: null,
1426
+ format: "",
1427
+ indent: 0,
1428
+ type: "link",
1429
+ version: 1
1430
+ };
1431
+ return linkNode;
1432
+ }
1433
+ };
1434
+
1435
+ // src/importFromHtml/ListItemNodeHandler.ts
1436
+ var import_node_html_parser13 = require("node-html-parser");
1437
+ var ListItemNodeHandler2 = class extends NodeHandler2 {
1438
+ processNode(node) {
1439
+ if (!(node instanceof import_node_html_parser13.HTMLElement)) return null;
1440
+ if (node.tagName !== "LI") return null;
1441
+ const jsonNode = {
1442
+ type: "listitem",
1443
+ version: 1,
1444
+ children: [],
1445
+ direction: "ltr",
1446
+ format: "",
1447
+ indent: 0,
1448
+ value: 1,
1449
+ // Default value, will be overridden by parent list
1450
+ checked: void 0
1451
+ };
1452
+ node.childNodes.forEach((child) => {
1453
+ jsonNode.children.push(...traverse2(child));
1454
+ });
1455
+ return jsonNode;
1456
+ }
1457
+ };
1458
+
1459
+ // src/importFromHtml/ListNodeHandler.ts
1460
+ var import_node_html_parser14 = require("node-html-parser");
1461
+ var ListNodeHandler2 = class extends NodeHandler2 {
1462
+ processNode(node) {
1463
+ if (!(node instanceof import_node_html_parser14.HTMLElement)) return null;
1464
+ if (node.tagName !== "UL" && node.tagName !== "OL") return null;
1465
+ const tag = node.tagName.toLowerCase();
1466
+ const listType = tag === "ol" ? "number" : "bullet";
1467
+ const start = 1;
1468
+ const jsonNode = {
1469
+ type: "list",
1470
+ version: 1,
1471
+ tag,
1472
+ listType,
1473
+ start,
1474
+ children: [],
1475
+ direction: null,
1476
+ format: "",
1477
+ indent: 0
1478
+ };
1479
+ let itemIndex = 1;
1480
+ node.childNodes.forEach((child) => {
1481
+ if (child instanceof import_node_html_parser14.HTMLElement && child.tagName === "LI") {
1482
+ const listItemNode = {
1483
+ type: "listitem",
1484
+ version: 1,
1485
+ children: [],
1486
+ direction: "ltr",
1487
+ format: "",
1488
+ indent: 0,
1489
+ value: itemIndex++,
1490
+ // Increment item index for each list item
1491
+ checked: void 0
1492
+ };
1493
+ child.childNodes.forEach((itemChild) => {
1494
+ const processedChildren = traverse2(itemChild);
1495
+ listItemNode.children.push(...processedChildren);
1496
+ });
1497
+ jsonNode.children.push(listItemNode);
1498
+ }
1499
+ });
1500
+ return jsonNode;
1501
+ }
1502
+ };
1503
+
1504
+ // src/importFromHtml/MatchingQuestionNodeHandler.ts
1505
+ var import_nanoid6 = require("nanoid");
1506
+ var import_node_html_parser15 = require("node-html-parser");
1507
+ var MatchingQuestionNodeHandler2 = class extends NodeHandler2 {
1508
+ processNode(node) {
1509
+ if (!(node instanceof import_node_html_parser15.HTMLElement) || node.tagName !== "x-matching".toUpperCase())
1510
+ return null;
1511
+ const jsonNode = {
1512
+ id: node.getAttribute("id") ?? (0, import_nanoid6.nanoid)(),
1513
+ pointsPerMatch: Number(
1514
+ node.getAttribute("data-x-points-per-match") ?? 1
1515
+ ),
1516
+ items: [],
1517
+ type: "matching-question",
1518
+ version: 1
1519
+ };
1520
+ const childNodes = node.childNodes;
1521
+ childNodes.forEach((child) => {
1522
+ if (!(child instanceof import_node_html_parser15.HTMLElement)) return;
1523
+ if (child.tagName === "x-match".toUpperCase()) {
1524
+ const premise = child.querySelector("x-premise");
1525
+ const option = child.querySelector("x-option");
1526
+ if (!premise || !option) return;
1527
+ jsonNode.items.push({
1528
+ id: premise.getAttribute("id") ?? (0, import_nanoid6.nanoid)(),
1529
+ correct: true,
1530
+ itemPremiseContent: createNestedEditorFromHtml(premise),
1531
+ itemOptionContent: createNestedEditorFromHtml(option)
1532
+ });
1533
+ }
1534
+ if (child.tagName === "x-distractor".toUpperCase()) {
1535
+ jsonNode.items.push({
1536
+ id: child.getAttribute("id") ?? (0, import_nanoid6.nanoid)(),
1537
+ correct: false,
1538
+ itemOptionContent: createNestedEditorFromHtml(child)
1539
+ });
1540
+ }
1541
+ });
1542
+ return jsonNode;
1543
+ }
1544
+ };
1545
+
1546
+ // src/importFromHtml/MultipleOptionQuestionNodeHandler.ts
1547
+ var import_nanoid7 = require("nanoid");
1548
+ var import_node_html_parser16 = require("node-html-parser");
1549
+ var MultipleOptionQuestionNodeHandler2 = class extends NodeHandler2 {
1550
+ processNode(node) {
1551
+ if (!(node instanceof import_node_html_parser16.HTMLElement)) return null;
1552
+ if (node.tagName === "x-multiple-choice".toUpperCase())
1553
+ return this.processMultipleChoiceNode(node);
1554
+ else if (node.tagName === "x-multiple-answers".toUpperCase())
1555
+ return this.processMultipleAnswersNode(node);
1556
+ else return null;
1557
+ }
1558
+ processMultipleChoiceNode(node) {
1559
+ const jsonNode = {
1560
+ options: [],
1561
+ id: node.getAttribute("id") ?? (0, import_nanoid7.nanoid)(),
1562
+ questionType: "multiple-choice",
1563
+ points: Number(node.getAttribute("data-x-points") || 0),
1564
+ choices: node.getAttribute("data-x-choices") ? Number(node.getAttribute("data-x-choices")) : void 0,
1565
+ noneOfTheAbove: node.getAttribute("data-x-none-of-the-above") === "true",
1566
+ type: "multiple-option-question",
1567
+ version: 1
1568
+ };
1569
+ this.processOptions(node, jsonNode);
1570
+ return jsonNode;
1571
+ }
1572
+ processMultipleAnswersNode(node) {
1573
+ const jsonNode = {
1574
+ options: [],
1575
+ id: node.getAttribute("id") ?? (0, import_nanoid7.nanoid)(),
1576
+ questionType: "multiple-answers",
1577
+ points: Number(node.getAttribute("data-x-points") || 0),
1578
+ correctChoices: node.getAttribute("data-x-correct-choices") ? Number(node.getAttribute("data-x-correct-choices")) : void 0,
1579
+ incorrectChoices: node.getAttribute("data-x-incorrect-choices") ? Number(node.getAttribute("data-x-incorrect-choices")) : void 0,
1580
+ grading: node.getAttribute("data-x-exact-match") === "true" ? "exactMatch" : node.getAttribute("data-x-exact-match") === "false" ? "partialCreditWithPenalty" : void 0,
1581
+ noneOfTheAbove: node.getAttribute("data-x-none-of-the-above") === "true",
1582
+ type: "multiple-option-question",
1583
+ version: 1
1584
+ };
1585
+ this.processOptions(node, jsonNode);
1586
+ return jsonNode;
1587
+ }
1588
+ processOptions(node, jsonNode) {
1589
+ const childNodes = node.childNodes;
1590
+ childNodes.forEach((child) => {
1591
+ if (!(child instanceof import_node_html_parser16.HTMLElement)) return;
1592
+ if (child.tagName !== "x-option".toUpperCase()) return;
1593
+ jsonNode.options.push({
1594
+ id: child.getAttribute("id") ?? (0, import_nanoid7.nanoid)(),
1595
+ correct: child.getAttribute("data-x-correct") === "true",
1596
+ content: createNestedEditorFromHtml(child)
1597
+ });
1598
+ });
1599
+ }
1600
+ };
1601
+
1602
+ // src/importFromHtml/ParagraphNodeHandler.ts
1603
+ var import_node_html_parser17 = require("node-html-parser");
1604
+ var TEXT_ALIGN_TO_FORMAT = {
1605
+ center: "center",
1606
+ end: "end",
1607
+ justify: "justify",
1608
+ left: "left",
1609
+ right: "right",
1610
+ start: "start"
1611
+ };
1612
+ var ParagraphNodeHandler2 = class extends NodeHandler2 {
1613
+ extractTextAlignment(element) {
1614
+ if (!element.hasAttribute("style")) return "";
1615
+ const styleAttr = element.getAttribute("style");
1616
+ const textAlignMatch = styleAttr?.match(/text-align:\s*([^;]+)/i);
1617
+ if (textAlignMatch && textAlignMatch[1]) {
1618
+ const textAlign = textAlignMatch[1].trim();
1619
+ if (textAlign in TEXT_ALIGN_TO_FORMAT) {
1620
+ return TEXT_ALIGN_TO_FORMAT[textAlign];
1621
+ }
1622
+ }
1623
+ return "";
1624
+ }
1625
+ processNode(node) {
1626
+ if (!(node instanceof import_node_html_parser17.HTMLElement)) return null;
1627
+ if (node.tagName !== "P") return null;
1628
+ const jsonNode = createEmptyParagraphNode();
1629
+ jsonNode.format = this.extractTextAlignment(node);
1630
+ if (node.childNodes.length === 1 && node.childNodes[0] instanceof import_node_html_parser17.HTMLElement && node.childNodes[0].tagName === "BR")
1631
+ return jsonNode;
1632
+ node.childNodes.forEach((child) => {
1633
+ const processedChildren = traverse2(child);
1634
+ jsonNode.children.push(...processedChildren);
1635
+ });
1636
+ return jsonNode;
1637
+ }
1638
+ };
1639
+
1640
+ // src/importFromHtml/ShortAnswerQuestionNodeHandler.ts
1641
+ var import_nanoid8 = require("nanoid");
1642
+ var import_node_html_parser18 = require("node-html-parser");
1643
+ var ShortAnswerQuestionNodeHandler2 = class extends NodeHandler2 {
1644
+ processNode(node) {
1645
+ if (!(node instanceof import_node_html_parser18.HTMLElement) || node.tagName !== "x-short-answer".toUpperCase())
1646
+ return null;
1647
+ return {
1648
+ id: node.getAttribute("id") ?? (0, import_nanoid8.nanoid)(),
1649
+ points: Number(node.getAttribute("data-x-points") ?? 1),
1650
+ maxWords: Number(node.getAttribute("data-x-max-words") ?? 100),
1651
+ notes: createNestedEditorFromHtml(
1652
+ node.querySelector("x-notes")
1653
+ ),
1654
+ type: "short-answer-question",
1655
+ version: 1
1656
+ };
1657
+ }
1658
+ };
1659
+
1660
+ // src/importFromHtml/SimulationQuestionNodeHandler.ts
1661
+ var import_node_html_parser19 = require("node-html-parser");
1662
+ var TAG_X_SIMULATION = "x-simulation";
1663
+ var TAG_X_SYSTEM_MESSAGE2 = "x-ai-system-message";
1664
+ var TAG_X_STEP2_INSTRUCTIONS = "x-step2-instructions";
1665
+ var SimulationQuestionNodeHandler2 = class extends NodeHandler2 {
1666
+ processNode(node) {
1667
+ if (!(node instanceof import_node_html_parser19.HTMLElement) || node.tagName !== TAG_X_SIMULATION.toUpperCase()) {
1668
+ return null;
1669
+ }
1670
+ const jsonNode = {
1671
+ id: node.getAttribute("id") || "",
1672
+ points: Number(node.getAttribute("data-x-points") || 0),
1673
+ aiChatModel: node.getAttribute("data-x-ai-chat-model") || null,
1674
+ step2Instructions: "",
1675
+ aiSystemMessage: "",
1676
+ type: "simulation-question",
1677
+ version: 1
1678
+ };
1679
+ const systemMessageElement = node.querySelector(
1680
+ TAG_X_SYSTEM_MESSAGE2
1681
+ );
1682
+ if (systemMessageElement)
1683
+ jsonNode.aiSystemMessage = systemMessageElement.innerHTML;
1684
+ const step2InstructionsElement = node.querySelector(
1685
+ TAG_X_STEP2_INSTRUCTIONS
1686
+ );
1687
+ if (step2InstructionsElement)
1688
+ jsonNode.step2Instructions = step2InstructionsElement.innerHTML;
1689
+ return jsonNode;
1690
+ }
1691
+ };
1692
+
1693
+ // src/importFromHtml/SpanNodeHandler.ts
1694
+ var import_node_html_parser20 = require("node-html-parser");
1695
+ var TextNode = import_node_html_parser20.parse.TextNode;
1696
+ var SpanNodeHandler = class extends NodeHandler2 {
1697
+ processNode(node) {
1698
+ if (!(node instanceof import_node_html_parser20.HTMLElement)) return null;
1699
+ if (node.tagName !== "SPAN") return null;
1700
+ if (!node.hasAttribute("style")) return null;
1701
+ const styleAttr = node.getAttribute("style");
1702
+ if (!styleAttr) return null;
1703
+ if (node.childNodes.length === 1 && node.childNodes[0] instanceof TextNode) {
1704
+ const textContent2 = node.text;
1705
+ return {
1706
+ detail: 0,
1707
+ format: 0,
1708
+ mode: "normal",
1709
+ style: styleAttr,
1710
+ text: textContent2,
1711
+ type: "text",
1712
+ version: 1
1713
+ };
1714
+ }
1715
+ let childResult = null;
1716
+ for (const childNode of node.childNodes) {
1717
+ const processedChildren = traverse2(childNode);
1718
+ for (const child of processedChildren) {
1719
+ if (child.type !== "text") continue;
1720
+ const textNode = child;
1721
+ if (!childResult) {
1722
+ childResult = {
1723
+ ...textNode,
1724
+ style: this.mergeStyles(textNode.style, styleAttr)
1725
+ };
1726
+ } else {
1727
+ childResult.text += textNode.text;
1728
+ childResult.format |= textNode.format || 0;
1729
+ if (textNode.style) {
1730
+ childResult.style = this.mergeStyles(
1731
+ childResult.style,
1732
+ textNode.style
1733
+ );
1734
+ }
1735
+ }
1736
+ }
1737
+ }
1738
+ if (childResult) {
1739
+ childResult.style = this.mergeStyles(
1740
+ childResult.style,
1741
+ styleAttr
1742
+ );
1743
+ return childResult;
1744
+ }
1745
+ const textContent = node.text;
1746
+ if (textContent.trim() !== "") {
1747
+ return {
1748
+ detail: 0,
1749
+ format: 0,
1750
+ mode: "normal",
1751
+ style: styleAttr,
1752
+ text: textContent,
1753
+ type: "text",
1754
+ version: 1
1755
+ };
1756
+ }
1757
+ return null;
1758
+ }
1759
+ mergeStyles(existingStyle, newStyle) {
1760
+ if (!existingStyle) return newStyle;
1761
+ if (!newStyle) return existingStyle;
1762
+ return `${existingStyle};${newStyle}`.replace(/;;/g, ";").replace(/^;/, "").replace(/;$/, "");
1763
+ }
1764
+ };
1765
+
1766
+ // src/importFromHtml/TableCellNodeHandler.ts
1767
+ var import_node_html_parser21 = require("node-html-parser");
1768
+
1769
+ // src/utils/styleUtils.ts
1770
+ var extractStyleValue = (styleAttr, property, isNumeric = false, unit = "") => {
1771
+ const regex = new RegExp(
1772
+ `${property}:[ ]*([^;]+)${unit ? unit : ""}`,
1773
+ "i"
1774
+ );
1775
+ const match = styleAttr.match(regex);
1776
+ if (!match || !match[1]) return null;
1777
+ const value = match[1].trim();
1778
+ return isNumeric ? parseFloat(value) : value;
1779
+ };
1780
+
1781
+ // src/importFromHtml/createTableNodes.ts
1782
+ function createEmptyTableNode() {
1783
+ return {
1784
+ direction: "ltr",
1785
+ format: "",
1786
+ indent: 0,
1787
+ type: "table",
1788
+ version: 1,
1789
+ children: []
1790
+ };
1791
+ }
1792
+ function createEmptyTableRowNode() {
1793
+ return {
1794
+ direction: "ltr",
1795
+ format: "",
1796
+ indent: 0,
1797
+ type: "tablerow",
1798
+ version: 1,
1799
+ children: []
1800
+ };
1801
+ }
1802
+ function createEmptyTableCellNode(isHeader = false) {
1803
+ return {
1804
+ direction: "ltr",
1805
+ format: "",
1806
+ indent: 0,
1807
+ type: "tablecell",
1808
+ version: 1,
1809
+ headerState: isHeader ? 1 : 0,
1810
+ children: []
1811
+ };
1812
+ }
1813
+
1814
+ // src/importFromHtml/TableCellNodeHandler.ts
1815
+ var isHtmlTableCellElement = (node) => {
1816
+ return node instanceof import_node_html_parser21.HTMLElement && (node.tagName === "TD" || node.tagName === "TH");
1817
+ };
1818
+ var TableCellNodeHandler2 = class extends NodeHandler2 {
1819
+ processNode(node) {
1820
+ if (!isHtmlTableCellElement(node)) {
1821
+ return null;
1822
+ }
1823
+ const isHeader = node.tagName === "TH";
1824
+ const cellNode = createEmptyTableCellNode(isHeader);
1825
+ cellNode.headerState = node.tagName === "TH" ? 1 : 0;
1826
+ const styleAttr = node.getAttribute("style") || "";
1827
+ const width = extractStyleValue(styleAttr, "width", true, "%");
1828
+ if (width !== null) {
1829
+ cellNode.width = width;
1830
+ }
1831
+ const backgroundColor = extractStyleValue(
1832
+ styleAttr,
1833
+ "background-color"
1834
+ );
1835
+ if (backgroundColor !== null) {
1836
+ cellNode.backgroundColor = backgroundColor;
1837
+ }
1838
+ const verticalAlign = extractStyleValue(
1839
+ styleAttr,
1840
+ "vertical-align"
1841
+ );
1842
+ if (verticalAlign !== null) {
1843
+ cellNode.verticalAlign = verticalAlign;
1844
+ }
1845
+ cellNode.children = createNestedNodesFromHtml(node);
1846
+ return cellNode;
1847
+ }
1848
+ };
1849
+
1850
+ // src/importFromHtml/TableNodeHandler.ts
1851
+ var import_node_html_parser22 = require("node-html-parser");
1852
+ var REFERENCE_TABLE_WIDTH = 720;
1853
+ var MINIMUM_REFERENCE_TABLE_WIDTH = 500;
1854
+ var isHtmlTableElement = (node) => node instanceof import_node_html_parser22.HTMLElement && node.tagName === "TABLE";
1855
+ var TableNodeHandler2 = class extends NodeHandler2 {
1856
+ processNode(node) {
1857
+ if (!isHtmlTableElement(node)) return null;
1858
+ const styleAttr = node.getAttribute("style") || "";
1859
+ const tableNode = createEmptyTableNode();
1860
+ tableNode.rowStriping = this.hasRowStriping(styleAttr);
1861
+ const effectiveReferenceWidth = this.getEffectiveReferenceWidth(styleAttr);
1862
+ this.processRows(node, tableNode);
1863
+ const targetRow = this.findTargetRowForWidths(node);
1864
+ if (targetRow) {
1865
+ tableNode.colWidths = this.extractColumnWidths(
1866
+ targetRow,
1867
+ effectiveReferenceWidth
1868
+ );
1869
+ }
1870
+ return tableNode;
1871
+ }
1872
+ hasRowStriping(styleAttr) {
1873
+ const borderCollapse = extractStyleValue(
1874
+ styleAttr,
1875
+ "border-collapse"
1876
+ );
1877
+ return borderCollapse === "collapse";
1878
+ }
1879
+ getEffectiveReferenceWidth(styleAttr) {
1880
+ const tableWidthPx = extractStyleValue(
1881
+ styleAttr,
1882
+ "width",
1883
+ true,
1884
+ "px"
1885
+ );
1886
+ const tableWidthPercent = extractStyleValue(
1887
+ styleAttr,
1888
+ "width",
1889
+ true,
1890
+ "%"
1891
+ );
1892
+ if (tableWidthPx && tableWidthPx > 0) {
1893
+ return tableWidthPx;
1894
+ }
1895
+ if (tableWidthPercent && tableWidthPercent > 0) {
1896
+ return Math.max(
1897
+ MINIMUM_REFERENCE_TABLE_WIDTH,
1898
+ tableWidthPercent / 100 * REFERENCE_TABLE_WIDTH
1899
+ );
1900
+ }
1901
+ return REFERENCE_TABLE_WIDTH;
1902
+ }
1903
+ processRows(node, tableNode) {
1904
+ const rows = node.querySelectorAll("tr");
1905
+ rows.forEach((row) => {
1906
+ const processedRows = traverse2(row);
1907
+ tableNode.children.push(...processedRows);
1908
+ });
1909
+ }
1910
+ findTargetRowForWidths(node) {
1911
+ return node.querySelector("thead tr") || node.querySelector("tbody tr") || node.querySelector("tr");
1912
+ }
1913
+ extractColumnWidths(targetRow, effectiveReferenceWidth) {
1914
+ const cells = targetRow.querySelectorAll("th, td");
1915
+ if (cells.length === 0) return [];
1916
+ const colWidths = [];
1917
+ let totalSpecifiedPercent = 0;
1918
+ let unspecifiedColumns = 0;
1919
+ cells.forEach((cell) => {
1920
+ const result = this.extractCellWidth(
1921
+ cell,
1922
+ effectiveReferenceWidth
1923
+ );
1924
+ colWidths.push(result.width);
1925
+ if (result.width > 0) {
1926
+ totalSpecifiedPercent += result.percentEquivalent;
1927
+ } else {
1928
+ unspecifiedColumns++;
1929
+ }
1930
+ });
1931
+ if (unspecifiedColumns > 0 && totalSpecifiedPercent < 100) {
1932
+ return this.distributeUnspecifiedWidths(
1933
+ colWidths,
1934
+ totalSpecifiedPercent,
1935
+ effectiveReferenceWidth,
1936
+ unspecifiedColumns
1937
+ );
1938
+ }
1939
+ return colWidths.some((width) => width > 0) ? colWidths : [];
1940
+ }
1941
+ extractCellWidth(cell, effectiveReferenceWidth) {
1942
+ const style = cell.getAttribute("style") || "";
1943
+ const widthPx = extractStyleValue(style, "width", true, "px");
1944
+ if (widthPx !== null && widthPx > 0) {
1945
+ return {
1946
+ width: Math.round(widthPx),
1947
+ // Calculate percentage equivalent for consistent distribution calculations
1948
+ percentEquivalent: widthPx / effectiveReferenceWidth * 100
1949
+ };
1950
+ }
1951
+ const widthPercent = extractStyleValue(
1952
+ style,
1953
+ "width",
1954
+ true,
1955
+ "%"
1956
+ );
1957
+ if (widthPercent !== null && widthPercent > 0) {
1958
+ return {
1959
+ width: Math.round(
1960
+ widthPercent / 100 * effectiveReferenceWidth
1961
+ ),
1962
+ percentEquivalent: widthPercent
1963
+ };
1964
+ }
1965
+ return { width: 0, percentEquivalent: 0 };
1966
+ }
1967
+ distributeUnspecifiedWidths(colWidths, totalSpecifiedPercent, effectiveReferenceWidth, unspecifiedColumns) {
1968
+ const remainingPercent = 100 - totalSpecifiedPercent;
1969
+ const percentPerColumn = remainingPercent / unspecifiedColumns;
1970
+ const pixelsPerColumn = Math.round(
1971
+ percentPerColumn / 100 * effectiveReferenceWidth
1972
+ );
1973
+ return colWidths.map(
1974
+ (width) => width === 0 ? pixelsPerColumn : width
1975
+ );
1976
+ }
1977
+ };
1978
+
1979
+ // src/importFromHtml/TableRowNodeHandler.ts
1980
+ var import_node_html_parser23 = require("node-html-parser");
1981
+ var isHtmlTableRowElement = (node) => {
1982
+ return node instanceof import_node_html_parser23.HTMLElement && node.tagName === "TR";
1983
+ };
1984
+ var TableRowNodeHandler2 = class extends NodeHandler2 {
1985
+ processNode(node) {
1986
+ if (!isHtmlTableRowElement(node)) {
1987
+ return null;
1988
+ }
1989
+ const rowNode = createEmptyTableRowNode();
1990
+ const cells = node.querySelectorAll("td, th");
1991
+ cells.forEach((cell) => {
1992
+ const processedCells = traverse2(cell);
1993
+ rowNode.children.push(...processedCells);
1994
+ });
1995
+ const styleAttr = node.getAttribute("style") || "";
1996
+ const height = extractStyleValue(styleAttr, "height", true, "px");
1997
+ if (height !== null) {
1998
+ rowNode.height = height;
1999
+ }
2000
+ return rowNode;
2001
+ }
2002
+ };
2003
+
2004
+ // src/importFromHtml/TextNodeHandler.ts
2005
+ var import_node_html_parser24 = require("node-html-parser");
2006
+ var TextNode2 = import_node_html_parser24.parse.TextNode;
2007
+ var TextNodeHandler2 = class extends NodeHandler2 {
2008
+ processNode(node) {
2009
+ if (node instanceof TextNode2 && !!node.parentNode.tagName) {
2010
+ if (node.isWhitespace) {
2011
+ return null;
2012
+ }
2013
+ const jsonNode = {
2014
+ detail: 0,
2015
+ format: 0,
2016
+ mode: "normal",
2017
+ style: "",
2018
+ text: node.text,
2019
+ type: "text",
2020
+ version: 1
2021
+ };
2022
+ return jsonNode;
2023
+ }
2024
+ return null;
2025
+ }
2026
+ };
2027
+
2028
+ // src/importFromHtml/VariableNodeHandler.ts
2029
+ var import_node_html_parser25 = require("node-html-parser");
2030
+ var VariableNodeHandler2 = class extends NodeHandler2 {
2031
+ processNode(node) {
2032
+ if (!(node instanceof import_node_html_parser25.HTMLElement) || !node.tagName) return null;
2033
+ if (node.tagName !== "x-param".toUpperCase()) return null;
2034
+ return {
2035
+ variableName: node.getAttribute("data-x-name") || "undefined",
2036
+ variableFormat: node.getAttribute("data-x-format"),
2037
+ type: "variable",
2038
+ version: 1
2039
+ };
2040
+ }
2041
+ };
2042
+
2043
+ // src/importFromHtml/traverse.ts
2044
+ var builder2 = new NodeHandlerChainBuilder();
2045
+ var nodeHandlerChain2 = builder2.addHandler(DivNodeHandler).addHandler(EssayQuestionNodeHandler2).addHandler(FillInTheBlankQuestionNodeHandler2).addHandler(FillInTheBlankSpaceNodeHandler2).addHandler(FinancialStatementQuestionNodeHandler2).addHandler(FormattedNodeHandler).addHandler(ImageNodeHandler2).addHandler(HorizontalRuleNodeHandler2).addHandler(JournalEntryQuestionNodeHandler2).addHandler(LineBreakNodeHandler2).addHandler(LinkNodeHandler2).addHandler(ListNodeHandler2).addHandler(ListItemNodeHandler2).addHandler(MatchingQuestionNodeHandler2).addHandler(MultipleOptionQuestionNodeHandler2).addHandler(ParagraphNodeHandler2).addHandler(ShortAnswerQuestionNodeHandler2).addHandler(SimulationQuestionNodeHandler2).addHandler(SpanNodeHandler).addHandler(TableCellNodeHandler2).addHandler(TableNodeHandler2).addHandler(TableRowNodeHandler2).addHandler(TextNodeHandler2).addHandler(VariableNodeHandler2).build();
2046
+ function traverse2(node) {
2047
+ const result = nodeHandlerChain2.handle(node);
2048
+ if (result === null) return [];
2049
+ if (Array.isArray(result)) return result;
2050
+ return [result];
2051
+ }
2052
+
2053
+ // src/importFromHtml/index.ts
2054
+ function processChildren(node) {
2055
+ const result = [];
2056
+ node.childNodes.forEach((node2) => {
2057
+ const processedChildren = traverse2(node2);
2058
+ result.push(...processedChildren);
2059
+ });
2060
+ return result;
2061
+ }
2062
+ function processHtml(html) {
2063
+ const root = (0, import_node_html_parser26.parse)(html);
2064
+ return processChildren(root);
2065
+ }
2066
+ function importFromHtml(html) {
2067
+ const children = processHtml(html);
2068
+ const rootJsonNode = {
2069
+ direction: "ltr",
2070
+ format: "",
2071
+ indent: 0,
2072
+ version: 0,
2073
+ type: "root",
2074
+ // Prevent Lexical's 'Ensure the editor state's root node never becomes empty' error
2075
+ children: children.length ? children : [createEmptyParagraphNode()]
2076
+ };
2077
+ return { root: rootJsonNode };
2078
+ }
2079
+ // Annotate the CommonJS export names for ESM import in node:
2080
+ 0 && (module.exports = {
2081
+ exportToHtml,
2082
+ importFromHtml
2083
+ });