@erudit-js/prose 4.0.0 → 4.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/app/composables/context.d.ts +6 -1
- package/dist/app/default/Inliners.vue +11 -11
- package/dist/app/default/Mix.vue +11 -11
- package/dist/app/default/Text.vue +25 -25
- package/dist/app/language/element.d.ts +1 -1
- package/dist/app/shared/Prose.vue +40 -40
- package/dist/app/shared/Render.vue +51 -51
- package/dist/app/shared/assets/block.svg +2 -2
- package/dist/app/shared/assets/check.svg +2 -2
- package/dist/app/shared/assets/inliner.svg +2 -2
- package/dist/app/shared/assets/plus.svg +2 -2
- package/dist/app/shared/block/AsideMenu.vue +44 -44
- package/dist/app/shared/block/AsideMenuButton.vue +51 -51
- package/dist/app/shared/block/AsideMenuCopyLink.vue +40 -40
- package/dist/app/shared/block/AsideMenuSeparator.vue +3 -3
- package/dist/app/shared/block/Block.vue +270 -270
- package/dist/app/shared/inliner/Inliner.vue +11 -11
- package/dist/app/shared/photoswipe/style.css +22 -22
- package/dist/elements/accent/Accent.vue +89 -88
- package/dist/elements/accent/AccentColumnSection.vue +61 -61
- package/dist/elements/accent/AccentRowSections.vue +64 -64
- package/dist/elements/callout/Callout.vue +81 -81
- package/dist/elements/callout/_global.d.ts +15 -15
- package/dist/elements/caption/Caption.vue +44 -44
- package/dist/elements/caption/_global.d.ts +26 -26
- package/dist/elements/details/Details.vue +49 -49
- package/dist/elements/details/_global.d.ts +27 -27
- package/dist/elements/details/icon.svg +2 -2
- package/dist/elements/diagram/Diagram.vue +356 -360
- package/dist/elements/diagram/_global.d.ts +19 -19
- package/dist/elements/emphasis/Emphasis.vue +25 -25
- package/dist/elements/emphasis/_global.d.ts +18 -18
- package/dist/elements/flex/Flex.vue +36 -36
- package/dist/elements/flex/_global.d.ts +23 -23
- package/dist/elements/gallery/Gallery.vue +56 -56
- package/dist/elements/gallery/_global.d.ts +18 -18
- package/dist/elements/heading/Heading.vue +44 -44
- package/dist/elements/heading/_global.d.ts +42 -42
- package/dist/elements/heading/icon.svg +2 -2
- package/dist/elements/horizontalLine/HorizontalLine.vue +6 -6
- package/dist/elements/horizontalLine/_global.d.ts +17 -17
- package/dist/elements/image/Image.vue +15 -15
- package/dist/elements/image/ImageElement.vue +80 -80
- package/dist/elements/image/_global.d.ts +18 -18
- package/dist/elements/lineBreak/LineBreak.vue +3 -3
- package/dist/elements/lineBreak/_global.d.ts +18 -18
- package/dist/elements/link/BlockLink.vue +159 -108
- package/dist/elements/link/Link.vue +119 -92
- package/dist/elements/link/core.js +1 -1
- package/dist/elements/link/dependency/_global.d.ts +47 -47
- package/dist/elements/link/dependency/app.d.ts +2 -2
- package/dist/elements/link/dependency/core.d.ts +12 -18
- package/dist/elements/link/dependency/core.js +1 -17
- package/dist/elements/link/reference/_global.d.ts +49 -49
- package/dist/elements/link/reference/languages/en.d.ts +2 -3
- package/dist/elements/link/reference/languages/en.js +5 -1
- package/dist/elements/link/reference/languages/ru.d.ts +2 -3
- package/dist/elements/link/reference/languages/ru.js +5 -1
- package/dist/elements/link/reference/phrases.d.ts +5 -0
- package/dist/elements/link/reference/phrases.js +1 -0
- package/dist/elements/link/step.d.ts +16 -0
- package/dist/elements/link/step.js +36 -0
- package/dist/elements/link/storage.d.ts +9 -5
- package/dist/elements/link/storage.js +4 -4
- package/dist/elements/list/List.vue +58 -58
- package/dist/elements/list/_global.d.ts +50 -50
- package/dist/elements/math/_global.d.ts +79 -72
- package/dist/elements/math/_global.ts +3 -3
- package/dist/elements/math/block.d.ts +2 -0
- package/dist/elements/math/block.js +42 -29
- package/dist/elements/math/components/BlockMath.vue +30 -30
- package/dist/elements/math/components/InlinerMath.vue +65 -65
- package/dist/elements/math/components/Katex.vue +88 -88
- package/dist/elements/math/components/MathGroup.vue +58 -41
- package/dist/elements/paragraph/Paragraph.vue +25 -25
- package/dist/elements/paragraph/_global.d.ts +27 -27
- package/dist/elements/paragraph/icon.svg +3 -3
- package/dist/elements/problem/_global.d.ts +257 -112
- package/dist/elements/problem/app.d.ts +7 -7
- package/dist/elements/problem/app.js +13 -12
- package/dist/elements/problem/assets/actions/answer.svg +2 -2
- package/dist/elements/problem/assets/actions/check.svg +2 -2
- package/dist/elements/problem/assets/actions/generate.svg +2 -2
- package/dist/elements/problem/assets/actions/hint.svg +2 -2
- package/dist/elements/problem/assets/actions/note.svg +2 -2
- package/dist/elements/problem/assets/actions/solution.svg +2 -2
- package/dist/elements/problem/assets/icon.svg +2 -2
- package/dist/elements/problem/components/Problem.vue +22 -22
- package/dist/elements/problem/components/ProblemButton.vue +20 -20
- package/dist/elements/problem/components/ProblemButtonGenerate.vue +96 -0
- package/dist/elements/problem/components/ProblemContainer.vue +8 -8
- package/dist/elements/problem/components/ProblemContent.vue +266 -356
- package/dist/elements/problem/components/ProblemExpander.vue +7 -7
- package/dist/elements/problem/components/ProblemExpanderSection.vue +57 -57
- package/dist/elements/problem/components/ProblemHeader.vue +100 -100
- package/dist/elements/problem/components/Problems.vue +83 -83
- package/dist/elements/problem/components/SubProblem.vue +14 -14
- package/dist/elements/problem/components/expanders/Check.vue +200 -153
- package/dist/elements/problem/components/expanders/Checks.vue +146 -146
- package/dist/elements/problem/components/expanders/DefaultPlusSections.vue +36 -38
- package/dist/elements/problem/components/expanders/Hint.vue +26 -26
- package/dist/elements/problem/core.d.ts +55 -28
- package/dist/elements/problem/core.js +2 -1
- package/dist/elements/problem/languages/{en.d.ts → problem/en.d.ts} +1 -1
- package/dist/elements/problem/languages/problem/en.js +6 -0
- package/dist/elements/problem/languages/{ru.d.ts → problem/ru.d.ts} +1 -1
- package/dist/elements/problem/languages/problem/ru.js +6 -0
- package/dist/elements/problem/languages/problems/en.d.ts +3 -0
- package/dist/elements/problem/languages/problems/en.js +6 -0
- package/dist/elements/problem/languages/problems/ru.d.ts +3 -0
- package/dist/elements/problem/languages/problems/ru.js +6 -0
- package/dist/elements/problem/languages/shared/en.d.ts +3 -0
- package/dist/elements/problem/languages/{en.js → shared/en.js} +12 -3
- package/dist/elements/problem/languages/shared/ru.d.ts +3 -0
- package/dist/elements/problem/languages/{ru.js → shared/ru.js} +12 -3
- package/dist/elements/problem/phrases.d.ts +4 -0
- package/dist/elements/problem/problemCheck.d.ts +166 -0
- package/dist/elements/problem/problemCheck.js +203 -0
- package/dist/elements/problem/problemContent.d.ts +2 -120
- package/dist/elements/problem/problemContent.js +2 -127
- package/dist/elements/problem/problemScript.d.ts +6 -1
- package/dist/elements/table/Table.vue +100 -100
- package/dist/elements/table/_global.d.ts +36 -36
- package/dist/elements/video/Video.vue +110 -110
- package/dist/elements/video/_global.d.ts +18 -18
- package/dist/resolve.d.ts +2 -1
- package/dist/resolve.js +8 -5
- package/package.json +4 -4
- package/types.d.ts +4 -4
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
import { defineRegistryItem, defineSchema, ensureTagChildren, ProseError } from "@jsprose/core";
|
|
2
|
+
import { defineEruditTag } from "../../tag.js";
|
|
3
|
+
import { defineEruditProseCoreElement } from "../../coreElement.js";
|
|
4
|
+
export const problemCheckSchema = defineSchema({
|
|
5
|
+
name: "problemCheck",
|
|
6
|
+
type: "block",
|
|
7
|
+
linkable: false
|
|
8
|
+
})();
|
|
9
|
+
export const ProblemCheck = defineEruditTag({
|
|
10
|
+
tagName: "ProblemCheck",
|
|
11
|
+
schema: problemCheckSchema
|
|
12
|
+
})(({ element, tagName, props, children }) => {
|
|
13
|
+
//
|
|
14
|
+
// Info
|
|
15
|
+
//
|
|
16
|
+
const checkInfo = {
|
|
17
|
+
label: props.label,
|
|
18
|
+
hint: props.hint,
|
|
19
|
+
placeholder: props.placeholder
|
|
20
|
+
};
|
|
21
|
+
//
|
|
22
|
+
// Children
|
|
23
|
+
//
|
|
24
|
+
if (children && children.length > 0) {
|
|
25
|
+
ensureTagChildren(tagName, children, problemCheckSchema);
|
|
26
|
+
element.children = children;
|
|
27
|
+
}
|
|
28
|
+
//
|
|
29
|
+
// Validator
|
|
30
|
+
//
|
|
31
|
+
let validator;
|
|
32
|
+
if ("yes" in props) {
|
|
33
|
+
validator = {
|
|
34
|
+
type: "boolean",
|
|
35
|
+
answer: true
|
|
36
|
+
};
|
|
37
|
+
} else if ("no" in props) {
|
|
38
|
+
validator = {
|
|
39
|
+
type: "boolean",
|
|
40
|
+
answer: false
|
|
41
|
+
};
|
|
42
|
+
} else if ("answer" in props) {
|
|
43
|
+
validator = {
|
|
44
|
+
type: "value",
|
|
45
|
+
answer: props.answer
|
|
46
|
+
};
|
|
47
|
+
} else if ("answers" in props) {
|
|
48
|
+
const answersProp = props.answers;
|
|
49
|
+
let ordered = false;
|
|
50
|
+
let separator = ",";
|
|
51
|
+
let answers;
|
|
52
|
+
if (Array.isArray(answersProp)) {
|
|
53
|
+
answers = answersProp;
|
|
54
|
+
} else {
|
|
55
|
+
ordered = answersProp.ordered ?? false;
|
|
56
|
+
separator = answersProp.separator ?? ",";
|
|
57
|
+
answers = answersProp.values;
|
|
58
|
+
}
|
|
59
|
+
validator = {
|
|
60
|
+
type: "array",
|
|
61
|
+
ordered,
|
|
62
|
+
separator,
|
|
63
|
+
answers
|
|
64
|
+
};
|
|
65
|
+
} else if ("script" in props) {
|
|
66
|
+
validator = {
|
|
67
|
+
type: "script",
|
|
68
|
+
name: props.script
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
element.data = {
|
|
72
|
+
...checkInfo,
|
|
73
|
+
serializedValidator: toSerializableValidator(validator)
|
|
74
|
+
};
|
|
75
|
+
});
|
|
76
|
+
export const problemCheckRegistryItem = defineRegistryItem({
|
|
77
|
+
schema: problemCheckSchema,
|
|
78
|
+
tags: [ProblemCheck]
|
|
79
|
+
});
|
|
80
|
+
export const problemCheckCoreElement = defineEruditProseCoreElement({ registryItem: problemCheckRegistryItem });
|
|
81
|
+
export function toSerializableValidator(validator) {
|
|
82
|
+
if (validator.type === "boolean") {
|
|
83
|
+
return validator;
|
|
84
|
+
}
|
|
85
|
+
const toSerializableValue = (value) => {
|
|
86
|
+
if (value instanceof RegExp) {
|
|
87
|
+
return {
|
|
88
|
+
type: "regexp",
|
|
89
|
+
source: value.source,
|
|
90
|
+
flags: value.flags
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
return value;
|
|
94
|
+
};
|
|
95
|
+
if (validator.type === "value") {
|
|
96
|
+
return {
|
|
97
|
+
type: "value",
|
|
98
|
+
answer: toSerializableValue(validator.answer)
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
if (validator.type === "array") {
|
|
102
|
+
return {
|
|
103
|
+
type: "array",
|
|
104
|
+
ordered: validator.ordered,
|
|
105
|
+
separator: validator.separator,
|
|
106
|
+
answers: validator.answers.map((answer) => {
|
|
107
|
+
if (Array.isArray(answer)) {
|
|
108
|
+
return answer.map(toSerializableValue);
|
|
109
|
+
}
|
|
110
|
+
return toSerializableValue(answer);
|
|
111
|
+
})
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
if (validator.type === "script") {
|
|
115
|
+
return validator;
|
|
116
|
+
}
|
|
117
|
+
throw new Error(`Unknown ProblemCheckData type "${validator.type}"!`);
|
|
118
|
+
}
|
|
119
|
+
export function fromSerializableValidator(serializedValidator) {
|
|
120
|
+
if (serializedValidator.type === "boolean") {
|
|
121
|
+
return serializedValidator;
|
|
122
|
+
}
|
|
123
|
+
const fromSerializableValue = (value) => {
|
|
124
|
+
if (value && value.type === "regexp") {
|
|
125
|
+
return new RegExp(value.source, value.flags);
|
|
126
|
+
}
|
|
127
|
+
return value;
|
|
128
|
+
};
|
|
129
|
+
if (serializedValidator.type === "value") {
|
|
130
|
+
return {
|
|
131
|
+
type: "value",
|
|
132
|
+
answer: fromSerializableValue(serializedValidator.answer)
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (serializedValidator.type === "array") {
|
|
136
|
+
return {
|
|
137
|
+
type: "array",
|
|
138
|
+
ordered: serializedValidator.ordered,
|
|
139
|
+
separator: serializedValidator.separator,
|
|
140
|
+
answers: serializedValidator.answers.map((answer) => {
|
|
141
|
+
if (Array.isArray(answer)) {
|
|
142
|
+
return answer.map(fromSerializableValue);
|
|
143
|
+
}
|
|
144
|
+
return fromSerializableValue(answer);
|
|
145
|
+
})
|
|
146
|
+
};
|
|
147
|
+
}
|
|
148
|
+
if (serializedValidator.type === "script") {
|
|
149
|
+
return serializedValidator;
|
|
150
|
+
}
|
|
151
|
+
throw new Error(`Unknown ProblemCheckData type "${serializedValidator.type}"!`);
|
|
152
|
+
}
|
|
153
|
+
export function checkProblemAnswer(answer, yesRegexp, noRegexp, validator) {
|
|
154
|
+
if (validator.type === "boolean") {
|
|
155
|
+
return validator.answer === true ? yesRegexp.test(answer) : noRegexp.test(answer);
|
|
156
|
+
}
|
|
157
|
+
const checkDefinedAnswer = (expected, answer) => {
|
|
158
|
+
if (typeof expected === "number") {
|
|
159
|
+
return Number(answer) === expected;
|
|
160
|
+
}
|
|
161
|
+
if (expected instanceof RegExp) {
|
|
162
|
+
return expected.test(answer);
|
|
163
|
+
}
|
|
164
|
+
return answer === String(expected);
|
|
165
|
+
};
|
|
166
|
+
const checkAnswer = (expect, answer) => {
|
|
167
|
+
if (expect === undefined) {
|
|
168
|
+
return answer.trim() === "";
|
|
169
|
+
}
|
|
170
|
+
return checkDefinedAnswer(expect, answer);
|
|
171
|
+
};
|
|
172
|
+
if (validator.type === "value") {
|
|
173
|
+
return checkAnswer(validator.answer, answer);
|
|
174
|
+
}
|
|
175
|
+
if (validator.type === "array") {
|
|
176
|
+
const separatorRegexp = new RegExp(`\\s*${validator.separator.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*`, "g");
|
|
177
|
+
const parts = answer.split(separatorRegexp).map((p) => p.trim());
|
|
178
|
+
const checkExpected = (expected, actual) => {
|
|
179
|
+
if (Array.isArray(expected)) {
|
|
180
|
+
// any-of logic
|
|
181
|
+
return expected.some((e) => checkDefinedAnswer(e, actual));
|
|
182
|
+
}
|
|
183
|
+
return checkDefinedAnswer(expected, actual);
|
|
184
|
+
};
|
|
185
|
+
if (parts.length !== validator.answers.length) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
if (validator.ordered) {
|
|
189
|
+
return validator.answers.every((expected, i) => checkExpected(expected, parts[i]));
|
|
190
|
+
}
|
|
191
|
+
// unordered matching (multiset semantics)
|
|
192
|
+
const remaining = [...parts];
|
|
193
|
+
for (const expected of validator.answers) {
|
|
194
|
+
const index = remaining.findIndex((actual) => checkExpected(expected, actual));
|
|
195
|
+
if (index === -1) {
|
|
196
|
+
return false;
|
|
197
|
+
}
|
|
198
|
+
remaining.splice(index, 1);
|
|
199
|
+
}
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
throw new ProseError(`"checkProblemAnswer" not implemented for type "${validator.type}"!`);
|
|
203
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { type AnySchema, type
|
|
1
|
+
import { type AnySchema, type NormalizedChildren, type TagChildren } from '@jsprose/core';
|
|
2
|
+
import { problemCheckSchema } from './problemCheck.js';
|
|
2
3
|
export declare const problemDescription: {
|
|
3
4
|
schema: {
|
|
4
5
|
name: "problemDescription";
|
|
@@ -425,124 +426,5 @@ export declare const problemAnswer: {
|
|
|
425
426
|
}, undefined>;
|
|
426
427
|
};
|
|
427
428
|
};
|
|
428
|
-
export type CheckFunction = (args: {
|
|
429
|
-
answer: string | undefined;
|
|
430
|
-
name: string;
|
|
431
|
-
answers: Record<string, string | undefined>;
|
|
432
|
-
}) => boolean;
|
|
433
|
-
export interface ProblemCheckRegex {
|
|
434
|
-
pattern: string;
|
|
435
|
-
flags: string;
|
|
436
|
-
}
|
|
437
|
-
export type ProblemCheckValue = string | ProblemCheckRegex | undefined;
|
|
438
|
-
export type ProblemCheckSetValue = string | ProblemCheckRegex;
|
|
439
|
-
export interface ProblemCheckData {
|
|
440
|
-
label?: string;
|
|
441
|
-
hint?: string;
|
|
442
|
-
placeholder?: string;
|
|
443
|
-
answers?: ProblemCheckValue[];
|
|
444
|
-
set?: {
|
|
445
|
-
separator: string;
|
|
446
|
-
values: (ProblemCheckSetValue | ProblemCheckSetValue[])[];
|
|
447
|
-
};
|
|
448
|
-
script?: string;
|
|
449
|
-
}
|
|
450
|
-
type Answer = string | number | RegExp | undefined;
|
|
451
|
-
export declare function checkValue(input: string | undefined, data: ProblemCheckData): boolean;
|
|
452
|
-
export declare const problemCheckSchema: {
|
|
453
|
-
name: "problemCheck";
|
|
454
|
-
type: "block";
|
|
455
|
-
linkable: false;
|
|
456
|
-
Data: ProblemCheckData;
|
|
457
|
-
Storage: undefined;
|
|
458
|
-
Children: BlockSchema[];
|
|
459
|
-
};
|
|
460
|
-
type UndefinedOnly<T> = {
|
|
461
|
-
[K in keyof T]?: undefined;
|
|
462
|
-
};
|
|
463
|
-
type OneOf<T extends Record<string, any>> = {
|
|
464
|
-
[K in keyof T]: Pick<T, K> & UndefinedOnly<Omit<T, K>>;
|
|
465
|
-
}[keyof T];
|
|
466
|
-
export declare const ProblemCheck: import("@jsprose/core").Tag<"ProblemCheck", {
|
|
467
|
-
name: "problemCheck";
|
|
468
|
-
type: "block";
|
|
469
|
-
linkable: false;
|
|
470
|
-
Data: ProblemCheckData;
|
|
471
|
-
Storage: undefined;
|
|
472
|
-
Children: BlockSchema[];
|
|
473
|
-
}, ({
|
|
474
|
-
label?: string;
|
|
475
|
-
hint?: string;
|
|
476
|
-
placeholder?: string;
|
|
477
|
-
} & (OneOf<{
|
|
478
|
-
answer: Answer;
|
|
479
|
-
answers: Answer[];
|
|
480
|
-
set: (Answer | Answer[])[] | {
|
|
481
|
-
separator: string;
|
|
482
|
-
values: (Answer | Answer[])[];
|
|
483
|
-
};
|
|
484
|
-
script: string;
|
|
485
|
-
}> & (TagChildren | NoTagChildren))) & {}>;
|
|
486
|
-
export declare const problemCheckRegistryItem: import("@jsprose/core").RegistryItem<{
|
|
487
|
-
name: "problemCheck";
|
|
488
|
-
type: "block";
|
|
489
|
-
linkable: false;
|
|
490
|
-
Data: ProblemCheckData;
|
|
491
|
-
Storage: undefined;
|
|
492
|
-
Children: BlockSchema[];
|
|
493
|
-
}, {
|
|
494
|
-
ProblemCheck: import("@jsprose/core").Tag<"ProblemCheck", {
|
|
495
|
-
name: "problemCheck";
|
|
496
|
-
type: "block";
|
|
497
|
-
linkable: false;
|
|
498
|
-
Data: ProblemCheckData;
|
|
499
|
-
Storage: undefined;
|
|
500
|
-
Children: BlockSchema[];
|
|
501
|
-
}, ({
|
|
502
|
-
label?: string;
|
|
503
|
-
hint?: string;
|
|
504
|
-
placeholder?: string;
|
|
505
|
-
} & (OneOf<{
|
|
506
|
-
answer: Answer;
|
|
507
|
-
answers: Answer[];
|
|
508
|
-
set: (Answer | Answer[])[] | {
|
|
509
|
-
separator: string;
|
|
510
|
-
values: (Answer | Answer[])[];
|
|
511
|
-
};
|
|
512
|
-
script: string;
|
|
513
|
-
}> & (TagChildren | NoTagChildren))) & {}>;
|
|
514
|
-
}, undefined>;
|
|
515
|
-
export declare const problemCheckCoreElement: {
|
|
516
|
-
registryItem: import("@jsprose/core").RegistryItem<{
|
|
517
|
-
name: "problemCheck";
|
|
518
|
-
type: "block";
|
|
519
|
-
linkable: false;
|
|
520
|
-
Data: ProblemCheckData;
|
|
521
|
-
Storage: undefined;
|
|
522
|
-
Children: BlockSchema[];
|
|
523
|
-
}, {
|
|
524
|
-
ProblemCheck: import("@jsprose/core").Tag<"ProblemCheck", {
|
|
525
|
-
name: "problemCheck";
|
|
526
|
-
type: "block";
|
|
527
|
-
linkable: false;
|
|
528
|
-
Data: ProblemCheckData;
|
|
529
|
-
Storage: undefined;
|
|
530
|
-
Children: BlockSchema[];
|
|
531
|
-
}, ({
|
|
532
|
-
label?: string;
|
|
533
|
-
hint?: string;
|
|
534
|
-
placeholder?: string;
|
|
535
|
-
} & (OneOf<{
|
|
536
|
-
answer: Answer;
|
|
537
|
-
answers: Answer[];
|
|
538
|
-
set: (Answer | Answer[])[] | {
|
|
539
|
-
separator: string;
|
|
540
|
-
values: (Answer | Answer[])[];
|
|
541
|
-
};
|
|
542
|
-
script: string;
|
|
543
|
-
}> & (TagChildren | NoTagChildren))) & {}>;
|
|
544
|
-
}, undefined>;
|
|
545
|
-
};
|
|
546
429
|
export type ProblemContentChild = typeof problemDescriptionSchema | typeof problemHintSchema | typeof problemNote.schema | typeof problemSolution.schema | typeof problemAnswer.schema | typeof problemCheckSchema;
|
|
547
430
|
export declare function validateProblemContent(source: string, children: NormalizedChildren): void;
|
|
548
|
-
export {};
|
|
@@ -1,20 +1,9 @@
|
|
|
1
|
-
|
|
2
|
-
// <Problem>
|
|
3
|
-
// <ProblemDescription />
|
|
4
|
-
// <ProblemHint />
|
|
5
|
-
// <ProblemNote>
|
|
6
|
-
// <ProblemSection title="..." />
|
|
7
|
-
// </ProblemNote>
|
|
8
|
-
// <ProblemSolution />
|
|
9
|
-
// <ProblemAnswer />
|
|
10
|
-
// <ProblemCheck />
|
|
11
|
-
// </Problem>
|
|
12
|
-
//
|
|
13
|
-
import { defineRegistryItem, defineSchema, ensureTagChildren, ensureTagNoChildren, isRawElement, ProseError } from "@jsprose/core";
|
|
1
|
+
import { defineRegistryItem, defineSchema, ensureTagChildren, isRawElement, ProseError } from "@jsprose/core";
|
|
14
2
|
import { defineEruditTag } from "../../tag.js";
|
|
15
3
|
import { defineEruditProseCoreElement } from "../../coreElement.js";
|
|
16
4
|
import { uppercaseFirst } from "../../utils/case.js";
|
|
17
5
|
import { tryParagraphWrap } from "../../shared/paragraphWrap.js";
|
|
6
|
+
import { problemCheckSchema } from "./problemCheck.js";
|
|
18
7
|
/* --------------------------------------------------------- */
|
|
19
8
|
/* shared helpers */
|
|
20
9
|
/* --------------------------------------------------------- */
|
|
@@ -128,120 +117,6 @@ function defineProblemSectionContainer(name) {
|
|
|
128
117
|
export const problemNote = defineProblemSectionContainer("note");
|
|
129
118
|
export const problemSolution = defineProblemSectionContainer("solution");
|
|
130
119
|
export const problemAnswer = defineProblemSectionContainer("answer");
|
|
131
|
-
function normalizeValue(v) {
|
|
132
|
-
if (v === undefined) return undefined;
|
|
133
|
-
if (v instanceof RegExp) {
|
|
134
|
-
return {
|
|
135
|
-
pattern: v.source,
|
|
136
|
-
flags: v.flags
|
|
137
|
-
};
|
|
138
|
-
}
|
|
139
|
-
return String(v);
|
|
140
|
-
}
|
|
141
|
-
function safeRegexTest(re, value) {
|
|
142
|
-
try {
|
|
143
|
-
return new RegExp(re.pattern, re.flags).test(value);
|
|
144
|
-
} catch {
|
|
145
|
-
return false;
|
|
146
|
-
}
|
|
147
|
-
}
|
|
148
|
-
function matchSingle(value, expected) {
|
|
149
|
-
// Handle null as undefined (from JSON serialization)
|
|
150
|
-
const normalizedExpected = expected === null ? undefined : expected;
|
|
151
|
-
const normalizedValue = value === null ? undefined : value;
|
|
152
|
-
if (normalizedExpected === undefined) return normalizedValue === undefined;
|
|
153
|
-
if (typeof normalizedExpected === "string") return normalizedValue === normalizedExpected;
|
|
154
|
-
if (normalizedValue === undefined) return false;
|
|
155
|
-
return safeRegexTest(normalizedExpected, normalizedValue);
|
|
156
|
-
}
|
|
157
|
-
function matchSet(value, set) {
|
|
158
|
-
const sep = new RegExp(`\\s*${set.separator.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*`);
|
|
159
|
-
const inputs = value.split(sep).filter(Boolean);
|
|
160
|
-
if (inputs.length !== set.values.length) return false;
|
|
161
|
-
const canMatch = (v, expected) => Array.isArray(expected) ? expected.some((e) => typeof e === "string" ? v === e : safeRegexTest(e, v)) : typeof expected === "string" ? v === expected : safeRegexTest(expected, v);
|
|
162
|
-
const adj = inputs.map((v) => set.values.map((e, i) => canMatch(v, e) ? i : -1).filter((i) => i !== -1));
|
|
163
|
-
const match = new Array(set.values.length).fill(-1);
|
|
164
|
-
const dfs = (u, seen) => adj[u].some((v) => {
|
|
165
|
-
if (seen[v]) return false;
|
|
166
|
-
seen[v] = true;
|
|
167
|
-
return match[v] === -1 || dfs(match[v], seen) ? (match[v] = u, true) : false;
|
|
168
|
-
});
|
|
169
|
-
return inputs.every((_, i) => dfs(i, new Array(set.values.length).fill(false)));
|
|
170
|
-
}
|
|
171
|
-
export function checkValue(input, data) {
|
|
172
|
-
const normalized = input?.trim().replace(/\s+/g, " ") || undefined;
|
|
173
|
-
if (data.answers) {
|
|
174
|
-
return data.answers.some((a) => matchSingle(normalized, a));
|
|
175
|
-
}
|
|
176
|
-
if (data.set && normalized !== undefined) {
|
|
177
|
-
return matchSet(normalized, data.set);
|
|
178
|
-
}
|
|
179
|
-
return false;
|
|
180
|
-
}
|
|
181
|
-
/* --------------------------------------------------------- */
|
|
182
|
-
/* problem check tag */
|
|
183
|
-
/* --------------------------------------------------------- */
|
|
184
|
-
export const problemCheckSchema = defineSchema({
|
|
185
|
-
name: "problemCheck",
|
|
186
|
-
type: "block",
|
|
187
|
-
linkable: false
|
|
188
|
-
})();
|
|
189
|
-
export const ProblemCheck = defineEruditTag({
|
|
190
|
-
tagName: "ProblemCheck",
|
|
191
|
-
schema: problemCheckSchema
|
|
192
|
-
})(({ element, tagName, props, children }) => {
|
|
193
|
-
if (children && children.length > 0) {
|
|
194
|
-
ensureTagChildren(tagName, children, problemCheckSchema);
|
|
195
|
-
element.children = children;
|
|
196
|
-
} else {
|
|
197
|
-
ensureTagNoChildren(tagName, children);
|
|
198
|
-
}
|
|
199
|
-
const data = {};
|
|
200
|
-
if ("answer" in props) {
|
|
201
|
-
data.answers = [normalizeValue(props.answer)];
|
|
202
|
-
} else if ("answers" in props) {
|
|
203
|
-
data.answers = props.answers.map(normalizeValue);
|
|
204
|
-
} else if ("script" in props) {
|
|
205
|
-
data.script = props.script;
|
|
206
|
-
} else if ("set" in props) {
|
|
207
|
-
const normalizeSetItem = (v) => {
|
|
208
|
-
if (Array.isArray(v)) {
|
|
209
|
-
return v.map((x) => {
|
|
210
|
-
const nv = normalizeValue(x);
|
|
211
|
-
if (!nv) {
|
|
212
|
-
throw new ProseError(`${tagName}: undefined not allowed in set values.`);
|
|
213
|
-
}
|
|
214
|
-
return nv;
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
const nv = normalizeValue(v);
|
|
218
|
-
if (!nv) {
|
|
219
|
-
throw new ProseError(`${tagName}: undefined not allowed in set values.`);
|
|
220
|
-
}
|
|
221
|
-
return nv;
|
|
222
|
-
};
|
|
223
|
-
if (Array.isArray(props.set)) {
|
|
224
|
-
data.set = {
|
|
225
|
-
separator: ",",
|
|
226
|
-
values: props.set.map(normalizeSetItem)
|
|
227
|
-
};
|
|
228
|
-
} else {
|
|
229
|
-
data.set = {
|
|
230
|
-
separator: props.set.separator,
|
|
231
|
-
values: props.set.values.map(normalizeSetItem)
|
|
232
|
-
};
|
|
233
|
-
}
|
|
234
|
-
}
|
|
235
|
-
if (props.label !== undefined) data.label = props.label;
|
|
236
|
-
if (props.hint !== undefined) data.hint = props.hint;
|
|
237
|
-
if (props.placeholder !== undefined) data.placeholder = props.placeholder;
|
|
238
|
-
element.data = data;
|
|
239
|
-
});
|
|
240
|
-
export const problemCheckRegistryItem = defineRegistryItem({
|
|
241
|
-
schema: problemCheckSchema,
|
|
242
|
-
tags: [ProblemCheck]
|
|
243
|
-
});
|
|
244
|
-
export const problemCheckCoreElement = defineEruditProseCoreElement({ registryItem: problemCheckRegistryItem });
|
|
245
120
|
export function validateProblemContent(source, children) {
|
|
246
121
|
ensureTagChildren(source, children, [
|
|
247
122
|
problemDescriptionSchema,
|
|
@@ -1,6 +1,11 @@
|
|
|
1
1
|
import { type AnySchema, type AnyUnique, type AutoUnique, type LinkableTag, type RawElement, type Unique, type WrapSchemas } from '@jsprose/core';
|
|
2
2
|
import { type ProblemSeed, ProblemRandom } from './rng.js';
|
|
3
|
-
import { type
|
|
3
|
+
import { type ProblemContentChild } from './problemContent.js';
|
|
4
|
+
export type CheckFunction = (args: {
|
|
5
|
+
answer: string | undefined;
|
|
6
|
+
name: string;
|
|
7
|
+
answers: Record<string, string | undefined>;
|
|
8
|
+
}) => boolean;
|
|
4
9
|
export declare function insertProblemScriptId(scriptSrc: string, code: string): string;
|
|
5
10
|
export interface ProblemScriptDefinition {
|
|
6
11
|
uniques?: Record<string, LinkableTag>;
|
|
@@ -1,100 +1,100 @@
|
|
|
1
|
-
<script lang="ts" setup>
|
|
2
|
-
import { computed } from 'vue';
|
|
3
|
-
import { isProseElement, type ProseElement } from '@jsprose/core';
|
|
4
|
-
|
|
5
|
-
import type { tableSchema } from './core.js';
|
|
6
|
-
import { captionSchema } from '../caption/core.js';
|
|
7
|
-
import Render from '../../app/shared/Render.vue';
|
|
8
|
-
import Block from '../../app/shared/block/Block.vue';
|
|
9
|
-
import Caption from '../caption/Caption.vue';
|
|
10
|
-
|
|
11
|
-
const { element } = defineProps<{
|
|
12
|
-
element: ProseElement<typeof tableSchema>;
|
|
13
|
-
}>();
|
|
14
|
-
|
|
15
|
-
const caption = computed(() => {
|
|
16
|
-
const maybeCaption = element.children[element.children.length - 1];
|
|
17
|
-
return isProseElement(maybeCaption, captionSchema) ? maybeCaption : undefined;
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const rows = computed(() =>
|
|
21
|
-
caption.value ? element.children.slice(0, -1) : element.children,
|
|
22
|
-
);
|
|
23
|
-
</script>
|
|
24
|
-
|
|
25
|
-
<template>
|
|
26
|
-
<Block :element>
|
|
27
|
-
<div class="nice-scrollbars overflow-auto">
|
|
28
|
-
<table
|
|
29
|
-
:class="[
|
|
30
|
-
$style.table,
|
|
31
|
-
`text-main-sm bg-bg-main m-auto max-w-full border-separate
|
|
32
|
-
border-spacing-[3px] rounded border border-(--tableBorder)`,
|
|
33
|
-
]"
|
|
34
|
-
>
|
|
35
|
-
<tbody>
|
|
36
|
-
<tr
|
|
37
|
-
v-for="row in rows"
|
|
38
|
-
:key="row.id"
|
|
39
|
-
class="odd:bg-(--oddCellBg) even:bg-(--evenCellBg)"
|
|
40
|
-
>
|
|
41
|
-
<td
|
|
42
|
-
v-for="cell in row.children"
|
|
43
|
-
:key="cell.id"
|
|
44
|
-
class="py-small px-normal rounded"
|
|
45
|
-
>
|
|
46
|
-
<Render
|
|
47
|
-
v-for="inliner of cell.children"
|
|
48
|
-
:key="inliner.id"
|
|
49
|
-
:element="inliner"
|
|
50
|
-
/>
|
|
51
|
-
</td>
|
|
52
|
-
</tr>
|
|
53
|
-
</tbody>
|
|
54
|
-
</table>
|
|
55
|
-
</div>
|
|
56
|
-
<Caption v-if="caption" :caption="caption" />
|
|
57
|
-
</Block>
|
|
58
|
-
</template>
|
|
59
|
-
|
|
60
|
-
<style module>
|
|
61
|
-
.table {
|
|
62
|
-
--tableBorder: color-mix(
|
|
63
|
-
in srgb,
|
|
64
|
-
var(--color-brand),
|
|
65
|
-
var(--color-border) 85%
|
|
66
|
-
);
|
|
67
|
-
|
|
68
|
-
--evenCellBg: color-mix(
|
|
69
|
-
in srgb,
|
|
70
|
-
light-dark(#f5f5f5, #282828),
|
|
71
|
-
var(--color-brand) 3%
|
|
72
|
-
);
|
|
73
|
-
|
|
74
|
-
--oddCellBg: color-mix(
|
|
75
|
-
in srgb,
|
|
76
|
-
light-dark(#f5f5f5, #282828),
|
|
77
|
-
var(--color-brand) 10%
|
|
78
|
-
);
|
|
79
|
-
|
|
80
|
-
[data-prose-accent] & {
|
|
81
|
-
--tableBorder: color-mix(
|
|
82
|
-
in srgb,
|
|
83
|
-
var(--accentBorder),
|
|
84
|
-
var(--color-border) 50%
|
|
85
|
-
);
|
|
86
|
-
|
|
87
|
-
--evenCellBg: color-mix(
|
|
88
|
-
in srgb,
|
|
89
|
-
light-dark(var(--color-bg-main), #202020),
|
|
90
|
-
var(--accentText) 12%
|
|
91
|
-
);
|
|
92
|
-
|
|
93
|
-
--oddCellBg: color-mix(
|
|
94
|
-
in srgb,
|
|
95
|
-
light-dark(var(--color-bg-main), #202020),
|
|
96
|
-
var(--accentText) 18%
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
</style>
|
|
1
|
+
<script lang="ts" setup>
|
|
2
|
+
import { computed } from 'vue';
|
|
3
|
+
import { isProseElement, type ProseElement } from '@jsprose/core';
|
|
4
|
+
|
|
5
|
+
import type { tableSchema } from './core.js';
|
|
6
|
+
import { captionSchema } from '../caption/core.js';
|
|
7
|
+
import Render from '../../app/shared/Render.vue';
|
|
8
|
+
import Block from '../../app/shared/block/Block.vue';
|
|
9
|
+
import Caption from '../caption/Caption.vue';
|
|
10
|
+
|
|
11
|
+
const { element } = defineProps<{
|
|
12
|
+
element: ProseElement<typeof tableSchema>;
|
|
13
|
+
}>();
|
|
14
|
+
|
|
15
|
+
const caption = computed(() => {
|
|
16
|
+
const maybeCaption = element.children[element.children.length - 1];
|
|
17
|
+
return isProseElement(maybeCaption, captionSchema) ? maybeCaption : undefined;
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
const rows = computed(() =>
|
|
21
|
+
caption.value ? element.children.slice(0, -1) : element.children,
|
|
22
|
+
);
|
|
23
|
+
</script>
|
|
24
|
+
|
|
25
|
+
<template>
|
|
26
|
+
<Block :element>
|
|
27
|
+
<div class="nice-scrollbars overflow-auto">
|
|
28
|
+
<table
|
|
29
|
+
:class="[
|
|
30
|
+
$style.table,
|
|
31
|
+
`text-main-sm bg-bg-main m-auto max-w-full border-separate
|
|
32
|
+
border-spacing-[3px] rounded border border-(--tableBorder)`,
|
|
33
|
+
]"
|
|
34
|
+
>
|
|
35
|
+
<tbody>
|
|
36
|
+
<tr
|
|
37
|
+
v-for="row in rows"
|
|
38
|
+
:key="row.id"
|
|
39
|
+
class="odd:bg-(--oddCellBg) even:bg-(--evenCellBg)"
|
|
40
|
+
>
|
|
41
|
+
<td
|
|
42
|
+
v-for="cell in row.children"
|
|
43
|
+
:key="cell.id"
|
|
44
|
+
class="py-small px-normal rounded"
|
|
45
|
+
>
|
|
46
|
+
<Render
|
|
47
|
+
v-for="inliner of cell.children"
|
|
48
|
+
:key="inliner.id"
|
|
49
|
+
:element="inliner"
|
|
50
|
+
/>
|
|
51
|
+
</td>
|
|
52
|
+
</tr>
|
|
53
|
+
</tbody>
|
|
54
|
+
</table>
|
|
55
|
+
</div>
|
|
56
|
+
<Caption v-if="caption" :caption="caption" />
|
|
57
|
+
</Block>
|
|
58
|
+
</template>
|
|
59
|
+
|
|
60
|
+
<style module>
|
|
61
|
+
.table {
|
|
62
|
+
--tableBorder: color-mix(
|
|
63
|
+
in srgb,
|
|
64
|
+
var(--color-brand),
|
|
65
|
+
var(--color-border) 85%
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
--evenCellBg: color-mix(
|
|
69
|
+
in srgb,
|
|
70
|
+
light-dark(#f5f5f5, #282828),
|
|
71
|
+
var(--color-brand) 3%
|
|
72
|
+
);
|
|
73
|
+
|
|
74
|
+
--oddCellBg: color-mix(
|
|
75
|
+
in srgb,
|
|
76
|
+
light-dark(#f5f5f5, #282828),
|
|
77
|
+
var(--color-brand) 10%
|
|
78
|
+
);
|
|
79
|
+
|
|
80
|
+
[data-prose-accent] & {
|
|
81
|
+
--tableBorder: color-mix(
|
|
82
|
+
in srgb,
|
|
83
|
+
var(--accentBorder),
|
|
84
|
+
var(--color-border) 50%
|
|
85
|
+
);
|
|
86
|
+
|
|
87
|
+
--evenCellBg: color-mix(
|
|
88
|
+
in srgb,
|
|
89
|
+
light-dark(var(--color-bg-main), #202020),
|
|
90
|
+
var(--accentText) 12%
|
|
91
|
+
);
|
|
92
|
+
|
|
93
|
+
--oddCellBg: color-mix(
|
|
94
|
+
in srgb,
|
|
95
|
+
light-dark(var(--color-bg-main), #202020),
|
|
96
|
+
var(--accentText) 18%
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
</style>
|