@mathscan/math-exercise-engine 1.1.10 → 1.1.12
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.d.ts +2 -0
- package/dist/index.js +901 -821
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +901 -821
- package/dist/index.mjs.map +1 -1
- package/dist/style.css +22 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3755,635 +3755,6 @@ function isProtractorWidget(widget) {
|
|
|
3755
3755
|
function isSetSquareWidget(widget) {
|
|
3756
3756
|
return widget.type === JSON_TYPES.SET_SQUARE;
|
|
3757
3757
|
}
|
|
3758
|
-
class LayoutIdGenerator {
|
|
3759
|
-
constructor() {
|
|
3760
|
-
this.counter = 0;
|
|
3761
|
-
}
|
|
3762
|
-
reset() {
|
|
3763
|
-
this.counter = 0;
|
|
3764
|
-
}
|
|
3765
|
-
generate(type) {
|
|
3766
|
-
return `${type}_${++this.counter}`;
|
|
3767
|
-
}
|
|
3768
|
-
}
|
|
3769
|
-
function toJSON(ast) {
|
|
3770
|
-
const idGenerator = new LayoutIdGenerator();
|
|
3771
|
-
const widgets = {};
|
|
3772
|
-
const answers = {};
|
|
3773
|
-
const allInlines = collectAllInlines(ast.lines);
|
|
3774
|
-
const inlineDragIds = /* @__PURE__ */ new Map();
|
|
3775
|
-
{
|
|
3776
|
-
let counter = 0;
|
|
3777
|
-
for (const inline of allInlines) {
|
|
3778
|
-
if (inline.type === AST_TYPES.INLINE_DRAG_OPTION) {
|
|
3779
|
-
inlineDragIds.set(inline, `${INLINE_DRAG_OPT_PREFIX}${counter++}`);
|
|
3780
|
-
}
|
|
3781
|
-
}
|
|
3782
|
-
}
|
|
3783
|
-
const layout = [];
|
|
3784
|
-
if (ast.question) {
|
|
3785
|
-
layout.push({
|
|
3786
|
-
id: idGenerator.generate(JSON_TYPES.QUESTION_TEXT),
|
|
3787
|
-
type: JSON_TYPES.QUESTION_TEXT,
|
|
3788
|
-
value: ast.question.value
|
|
3789
|
-
});
|
|
3790
|
-
}
|
|
3791
|
-
for (const block of ast.lines) {
|
|
3792
|
-
if (block.type === AST_TYPES.TABLE_BLOCK) {
|
|
3793
|
-
layout.push(transformTable(block, idGenerator, inlineDragIds));
|
|
3794
|
-
} else if (block.type === AST_TYPES.NUMBER_LINE_BLOCK) {
|
|
3795
|
-
const nlItem = transformNumberLine(block, idGenerator, inlineDragIds);
|
|
3796
|
-
layout.push(nlItem);
|
|
3797
|
-
if (nlItem.counter?.answer !== void 0) {
|
|
3798
|
-
answers[nlItem.counter.id] = nlItem.counter.answer;
|
|
3799
|
-
}
|
|
3800
|
-
for (const cursor of nlItem.cursors ?? []) {
|
|
3801
|
-
answers[cursor.id] = cursor.answer;
|
|
3802
|
-
}
|
|
3803
|
-
} else if (block.type === AST_TYPES.MATCHER_BLOCK) {
|
|
3804
|
-
const matcherBlock = block;
|
|
3805
|
-
layout.push({
|
|
3806
|
-
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
3807
|
-
type: JSON_TYPES.WIDGET_REF,
|
|
3808
|
-
widgetId: matcherBlock.name
|
|
3809
|
-
});
|
|
3810
|
-
layout.push({
|
|
3811
|
-
id: idGenerator.generate(JSON_TYPES.LINEBREAK),
|
|
3812
|
-
type: JSON_TYPES.LINEBREAK
|
|
3813
|
-
});
|
|
3814
|
-
} else if (block.type === AST_TYPES.PAIR_MATCHER_BLOCK) {
|
|
3815
|
-
const pmBlock = block;
|
|
3816
|
-
layout.push({
|
|
3817
|
-
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
3818
|
-
type: JSON_TYPES.WIDGET_REF,
|
|
3819
|
-
widgetId: pmBlock.name
|
|
3820
|
-
});
|
|
3821
|
-
layout.push({
|
|
3822
|
-
id: idGenerator.generate(JSON_TYPES.LINEBREAK),
|
|
3823
|
-
type: JSON_TYPES.LINEBREAK
|
|
3824
|
-
});
|
|
3825
|
-
} else if (block.type === AST_TYPES.VERTICAL_OPERATION_BLOCK) {
|
|
3826
|
-
layout.push(transformVerticalOperation(block, idGenerator));
|
|
3827
|
-
} else if (block.type === AST_TYPES.RADIO_OPTIONS) {
|
|
3828
|
-
const radioBlock = block;
|
|
3829
|
-
layout.push({
|
|
3830
|
-
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
3831
|
-
type: JSON_TYPES.WIDGET_REF,
|
|
3832
|
-
widgetId: radioBlock.name
|
|
3833
|
-
});
|
|
3834
|
-
layout.push({
|
|
3835
|
-
id: idGenerator.generate(JSON_TYPES.LINEBREAK),
|
|
3836
|
-
type: JSON_TYPES.LINEBREAK
|
|
3837
|
-
});
|
|
3838
|
-
} else {
|
|
3839
|
-
const lineItems = transformLine(block, idGenerator, inlineDragIds);
|
|
3840
|
-
layout.push(...lineItems);
|
|
3841
|
-
}
|
|
3842
|
-
}
|
|
3843
|
-
for (const inline of allInlines) {
|
|
3844
|
-
if (inline.type === AST_TYPES.INLINE_INPUT) {
|
|
3845
|
-
if (inline.inputType === INPUT_TYPES.STRING) {
|
|
3846
|
-
widgets[inline.name] = {
|
|
3847
|
-
type: JSON_TYPES.INLINE_INPUT,
|
|
3848
|
-
inputType: INPUT_TYPES.STRING,
|
|
3849
|
-
length: inline.length,
|
|
3850
|
-
placeholder: inline.placeholder
|
|
3851
|
-
};
|
|
3852
|
-
} else {
|
|
3853
|
-
widgets[inline.name] = {
|
|
3854
|
-
type: JSON_TYPES.INLINE_INPUT,
|
|
3855
|
-
inputType: INPUT_TYPES.NUMERIC,
|
|
3856
|
-
digits: inline.digits,
|
|
3857
|
-
placeholder: inline.placeholder
|
|
3858
|
-
};
|
|
3859
|
-
}
|
|
3860
|
-
if (inline.answer !== void 0) {
|
|
3861
|
-
answers[inline.name] = inline.answer;
|
|
3862
|
-
}
|
|
3863
|
-
} else if (inline.type === AST_TYPES.CLOCK && inline.mode === "interactive") {
|
|
3864
|
-
answers[inline.name] = inline.answer;
|
|
3865
|
-
}
|
|
3866
|
-
if (inline.type === AST_TYPES.FRACTION) {
|
|
3867
|
-
widgets[inline.name] = {
|
|
3868
|
-
type: JSON_TYPES.FRACTION,
|
|
3869
|
-
numerator: inline.numerator ?? null,
|
|
3870
|
-
denominator: inline.denominator ?? null,
|
|
3871
|
-
simplified: inline.simplified ?? true,
|
|
3872
|
-
placeholder: inline.placeholder
|
|
3873
|
-
};
|
|
3874
|
-
if (inline.answer !== void 0) {
|
|
3875
|
-
answers[inline.name] = inline.answer;
|
|
3876
|
-
}
|
|
3877
|
-
}
|
|
3878
|
-
if (inline.type === AST_TYPES.INLINE_DRAG_OPTION) {
|
|
3879
|
-
const widgetId = inlineDragIds.get(inline);
|
|
3880
|
-
widgets[widgetId] = {
|
|
3881
|
-
type: JSON_TYPES.INLINE_DRAG_OPTION,
|
|
3882
|
-
value: inline.value
|
|
3883
|
-
};
|
|
3884
|
-
}
|
|
3885
|
-
if (inline.type === AST_TYPES.VISUAL_FRACTION) {
|
|
3886
|
-
const answerArr = inline.answer ?? [];
|
|
3887
|
-
widgets[inline.name] = {
|
|
3888
|
-
type: JSON_TYPES.VISUAL_FRACTION,
|
|
3889
|
-
shape: inline.shape,
|
|
3890
|
-
divisions: inline.divisions,
|
|
3891
|
-
filled: inline.filled ?? [],
|
|
3892
|
-
answer: answerArr,
|
|
3893
|
-
...inline.orientation !== void 0 && { orientation: inline.orientation },
|
|
3894
|
-
...inline.label !== void 0 && { label: inline.label },
|
|
3895
|
-
...inline.size !== void 0 && { size: inline.size }
|
|
3896
|
-
};
|
|
3897
|
-
if (answerArr.length > 0) {
|
|
3898
|
-
answers[inline.name] = answerArr;
|
|
3899
|
-
}
|
|
3900
|
-
}
|
|
3901
|
-
}
|
|
3902
|
-
for (const block of ast.lines) {
|
|
3903
|
-
if (block.type !== AST_TYPES.VERTICAL_OPERATION_BLOCK) continue;
|
|
3904
|
-
const vop = block;
|
|
3905
|
-
for (const item of vop.items) {
|
|
3906
|
-
if (item.type !== AST_TYPES.VOP_ROW && item.type !== AST_TYPES.VOP_CARRY_ROW) continue;
|
|
3907
|
-
for (const cell of item.cells) {
|
|
3908
|
-
if (cell.kind !== "input") continue;
|
|
3909
|
-
if (widgets[cell.name]) {
|
|
3910
|
-
throw new Error(`VERTICAL_OPERATION: duplicate INPUT name "${cell.name}"`);
|
|
3911
|
-
}
|
|
3912
|
-
widgets[cell.name] = {
|
|
3913
|
-
type: JSON_TYPES.INLINE_INPUT,
|
|
3914
|
-
inputType: INPUT_TYPES.NUMERIC,
|
|
3915
|
-
digits: cell.digits
|
|
3916
|
-
};
|
|
3917
|
-
if (cell.answer !== void 0) {
|
|
3918
|
-
answers[cell.name] = cell.answer;
|
|
3919
|
-
}
|
|
3920
|
-
}
|
|
3921
|
-
}
|
|
3922
|
-
}
|
|
3923
|
-
for (const block of ast.lines) {
|
|
3924
|
-
if (block.type === AST_TYPES.RADIO_OPTIONS) {
|
|
3925
|
-
const radioBlock = block;
|
|
3926
|
-
widgets[radioBlock.name] = {
|
|
3927
|
-
type: JSON_TYPES.RADIO_OPTIONS,
|
|
3928
|
-
options: radioBlock.options
|
|
3929
|
-
};
|
|
3930
|
-
answers[radioBlock.name] = radioBlock.answer;
|
|
3931
|
-
} else if (block.type === AST_TYPES.MATCHER_BLOCK) {
|
|
3932
|
-
const matcherBlock = block;
|
|
3933
|
-
widgets[matcherBlock.name] = buildMatcherWidget(matcherBlock, idGenerator, inlineDragIds);
|
|
3934
|
-
answers[matcherBlock.name] = matcherBlock.answers;
|
|
3935
|
-
} else if (block.type === AST_TYPES.PAIR_MATCHER_BLOCK) {
|
|
3936
|
-
const pmBlock = block;
|
|
3937
|
-
widgets[pmBlock.name] = buildPairMatcherWidget(pmBlock, idGenerator, inlineDragIds);
|
|
3938
|
-
answers[pmBlock.name] = pmBlock.answers;
|
|
3939
|
-
}
|
|
3940
|
-
}
|
|
3941
|
-
if (ast.tools) {
|
|
3942
|
-
for (const tool of ast.tools) {
|
|
3943
|
-
if (tool.type === AST_TYPES.DRAG_OPTIONS) {
|
|
3944
|
-
widgets["_drag_options"] = {
|
|
3945
|
-
type: JSON_TYPES.DRAG_OPTIONS,
|
|
3946
|
-
options: tool.options
|
|
3947
|
-
};
|
|
3948
|
-
} else if (tool.type === AST_TYPES.NUMERIC_KEYPAD) {
|
|
3949
|
-
widgets["_numeric_keypad"] = {
|
|
3950
|
-
type: JSON_TYPES.NUMERIC_KEYPAD,
|
|
3951
|
-
includeOperators: tool.includeOperators,
|
|
3952
|
-
includeDecimal: tool.includeDecimal,
|
|
3953
|
-
style: tool.style
|
|
3954
|
-
};
|
|
3955
|
-
} else if (tool.type === AST_TYPES.GEOMETRY_RULER) {
|
|
3956
|
-
widgets[RULER_WIDGET_ID] = {
|
|
3957
|
-
type: JSON_TYPES.RULER,
|
|
3958
|
-
size: tool.size
|
|
3959
|
-
};
|
|
3960
|
-
} else if (tool.type === AST_TYPES.GEOMETRY_PROTRACTOR) {
|
|
3961
|
-
widgets[PROTRACTOR_WIDGET_ID] = {
|
|
3962
|
-
type: JSON_TYPES.PROTRACTOR,
|
|
3963
|
-
size: tool.size
|
|
3964
|
-
};
|
|
3965
|
-
} else if (tool.type === AST_TYPES.GEOMETRY_SET_SQUARE) {
|
|
3966
|
-
widgets[SET_SQUARE_WIDGET_ID] = {
|
|
3967
|
-
type: JSON_TYPES.SET_SQUARE,
|
|
3968
|
-
size: tool.size
|
|
3969
|
-
};
|
|
3970
|
-
}
|
|
3971
|
-
}
|
|
3972
|
-
}
|
|
3973
|
-
return {
|
|
3974
|
-
version: "2.0",
|
|
3975
|
-
// Natural DSL version
|
|
3976
|
-
layout,
|
|
3977
|
-
widgets,
|
|
3978
|
-
...ast.validation ? { validation: transformValidation(ast.validation, answers, widgets) } : {}
|
|
3979
|
-
};
|
|
3980
|
-
}
|
|
3981
|
-
function collectAllInlines(blocks) {
|
|
3982
|
-
const result = [];
|
|
3983
|
-
for (const block of blocks) {
|
|
3984
|
-
if (block.type === AST_TYPES.LINE_BLOCK) {
|
|
3985
|
-
result.push(...block.inlines);
|
|
3986
|
-
} else if (block.type === AST_TYPES.TABLE_BLOCK) {
|
|
3987
|
-
for (const row of block.rows) {
|
|
3988
|
-
for (const cell of row.cells) {
|
|
3989
|
-
result.push(...cell.inlines);
|
|
3990
|
-
}
|
|
3991
|
-
}
|
|
3992
|
-
} else if (block.type === AST_TYPES.NUMBER_LINE_BLOCK) {
|
|
3993
|
-
const numberLine = block;
|
|
3994
|
-
for (const position of numberLine.positions) {
|
|
3995
|
-
result.push(...position.inlines);
|
|
3996
|
-
}
|
|
3997
|
-
for (const arrow of numberLine.arrows ?? []) {
|
|
3998
|
-
result.push(...arrow.inlines);
|
|
3999
|
-
}
|
|
4000
|
-
} else if (block.type === AST_TYPES.MATCHER_BLOCK) {
|
|
4001
|
-
for (const row of block.rows) {
|
|
4002
|
-
for (const item of row.items) {
|
|
4003
|
-
result.push(...item.inlines);
|
|
4004
|
-
}
|
|
4005
|
-
}
|
|
4006
|
-
} else if (block.type === AST_TYPES.PAIR_MATCHER_BLOCK) {
|
|
4007
|
-
const pm = block;
|
|
4008
|
-
for (const item of [...pm.topItems, ...pm.bottomItems]) {
|
|
4009
|
-
result.push(...item.inlines);
|
|
4010
|
-
}
|
|
4011
|
-
}
|
|
4012
|
-
}
|
|
4013
|
-
return result;
|
|
4014
|
-
}
|
|
4015
|
-
function transformNumberLine(numberLine, idGenerator, inlineDragIds) {
|
|
4016
|
-
const positions = numberLine.positions.map((pos) => {
|
|
4017
|
-
const content = pos.inlines.map((inline) => transformInline(inline, idGenerator, inlineDragIds));
|
|
4018
|
-
return {
|
|
4019
|
-
id: idGenerator.generate(JSON_TYPES.NUMBER_LINE_POSITION),
|
|
4020
|
-
type: JSON_TYPES.NUMBER_LINE_POSITION,
|
|
4021
|
-
value: pos.value,
|
|
4022
|
-
content
|
|
4023
|
-
};
|
|
4024
|
-
});
|
|
4025
|
-
let counterItem;
|
|
4026
|
-
if (numberLine.counter !== void 0) {
|
|
4027
|
-
const c = numberLine.counter;
|
|
4028
|
-
counterItem = {
|
|
4029
|
-
id: idGenerator.generate(JSON_TYPES.NUMBER_LINE_COUNTER),
|
|
4030
|
-
type: JSON_TYPES.NUMBER_LINE_COUNTER,
|
|
4031
|
-
min: c.min,
|
|
4032
|
-
max: c.max,
|
|
4033
|
-
...c.default !== void 0 && { default: c.default },
|
|
4034
|
-
...c.name !== void 0 && { name: c.name },
|
|
4035
|
-
...c.answer !== void 0 && { answer: c.answer }
|
|
4036
|
-
};
|
|
4037
|
-
}
|
|
4038
|
-
let highlights;
|
|
4039
|
-
if (numberLine.highlights !== void 0 && numberLine.highlights.length > 0) {
|
|
4040
|
-
highlights = numberLine.highlights.map((h) => ({
|
|
4041
|
-
id: idGenerator.generate(JSON_TYPES.NUMBER_LINE_HIGHLIGHT),
|
|
4042
|
-
type: JSON_TYPES.NUMBER_LINE_HIGHLIGHT,
|
|
4043
|
-
from: h.from,
|
|
4044
|
-
to: h.to,
|
|
4045
|
-
...h.color !== void 0 && { color: h.color },
|
|
4046
|
-
...h.label !== void 0 && { label: h.label }
|
|
4047
|
-
}));
|
|
4048
|
-
}
|
|
4049
|
-
let cursors;
|
|
4050
|
-
if (numberLine.cursors !== void 0 && numberLine.cursors.length > 0) {
|
|
4051
|
-
cursors = numberLine.cursors.map((c) => ({
|
|
4052
|
-
id: idGenerator.generate(JSON_TYPES.NUMBER_LINE_CURSOR),
|
|
4053
|
-
type: JSON_TYPES.NUMBER_LINE_CURSOR,
|
|
4054
|
-
value: c.value,
|
|
4055
|
-
answer: c.answer,
|
|
4056
|
-
fillRange: c.fillRange,
|
|
4057
|
-
...c.color !== void 0 && { color: c.color },
|
|
4058
|
-
...c.label !== void 0 && { label: c.label }
|
|
4059
|
-
}));
|
|
4060
|
-
}
|
|
4061
|
-
let arrows;
|
|
4062
|
-
if (numberLine.arrows !== void 0 && numberLine.arrows.length > 0) {
|
|
4063
|
-
arrows = numberLine.arrows.map((a) => ({
|
|
4064
|
-
id: idGenerator.generate(JSON_TYPES.NUMBER_LINE_ARROW),
|
|
4065
|
-
type: JSON_TYPES.NUMBER_LINE_ARROW,
|
|
4066
|
-
from: a.from,
|
|
4067
|
-
to: a.to,
|
|
4068
|
-
...a.color !== void 0 && { color: a.color },
|
|
4069
|
-
content: a.inlines.map((inline) => transformInline(inline, idGenerator, inlineDragIds))
|
|
4070
|
-
}));
|
|
4071
|
-
}
|
|
4072
|
-
const item = {
|
|
4073
|
-
id: idGenerator.generate(JSON_TYPES.NUMBER_LINE),
|
|
4074
|
-
type: JSON_TYPES.NUMBER_LINE,
|
|
4075
|
-
min: numberLine.min,
|
|
4076
|
-
max: numberLine.max,
|
|
4077
|
-
step: numberLine.step,
|
|
4078
|
-
...numberLine.minorTicks !== void 0 && { minorTicks: numberLine.minorTicks },
|
|
4079
|
-
...numberLine.style !== void 0 && { style: numberLine.style },
|
|
4080
|
-
...numberLine.labelVisibility !== void 0 && { labelVisibility: numberLine.labelVisibility },
|
|
4081
|
-
...counterItem !== void 0 && { counter: counterItem },
|
|
4082
|
-
...highlights !== void 0 && { highlights },
|
|
4083
|
-
...cursors !== void 0 && { cursors },
|
|
4084
|
-
...arrows !== void 0 && { arrows },
|
|
4085
|
-
...numberLine.dragMode !== void 0 && { dragMode: numberLine.dragMode },
|
|
4086
|
-
positions
|
|
4087
|
-
};
|
|
4088
|
-
return item;
|
|
4089
|
-
}
|
|
4090
|
-
function mapMatcherItemList(items, idGenerator, inlineDragIds) {
|
|
4091
|
-
return items.map((item) => ({
|
|
4092
|
-
id: idGenerator.generate(JSON_TYPES.MATCHER_ITEM),
|
|
4093
|
-
type: JSON_TYPES.MATCHER_ITEM,
|
|
4094
|
-
itemId: item.itemId,
|
|
4095
|
-
content: item.inlines.map((inline) => transformInline(inline, idGenerator, inlineDragIds))
|
|
4096
|
-
}));
|
|
4097
|
-
}
|
|
4098
|
-
function buildMatcherWidget(matcher, idGenerator, inlineDragIds) {
|
|
4099
|
-
const rows = matcher.rows.map((row) => ({
|
|
4100
|
-
id: idGenerator.generate(JSON_TYPES.MATCHER_ROW),
|
|
4101
|
-
type: JSON_TYPES.MATCHER_ROW,
|
|
4102
|
-
items: mapMatcherItemList(row.items, idGenerator, inlineDragIds)
|
|
4103
|
-
}));
|
|
4104
|
-
const result = {
|
|
4105
|
-
type: JSON_TYPES.MATCHER,
|
|
4106
|
-
rows,
|
|
4107
|
-
groupCount: matcher.answers.length
|
|
4108
|
-
};
|
|
4109
|
-
if (matcher.colors !== void 0) {
|
|
4110
|
-
result.colors = matcher.colors;
|
|
4111
|
-
}
|
|
4112
|
-
return result;
|
|
4113
|
-
}
|
|
4114
|
-
function buildPairMatcherWidget(pm, idGenerator, inlineDragIds) {
|
|
4115
|
-
return {
|
|
4116
|
-
type: JSON_TYPES.PAIR_MATCHER,
|
|
4117
|
-
topItems: mapMatcherItemList(pm.topItems, idGenerator, inlineDragIds),
|
|
4118
|
-
bottomItems: mapMatcherItemList(pm.bottomItems, idGenerator, inlineDragIds)
|
|
4119
|
-
};
|
|
4120
|
-
}
|
|
4121
|
-
function transformVerticalOperation(vop, idGenerator) {
|
|
4122
|
-
const anyRowHasOp = vop.items.some(
|
|
4123
|
-
(item) => item.type === AST_TYPES.VOP_ROW && item.op !== void 0
|
|
4124
|
-
);
|
|
4125
|
-
if (!anyRowHasOp && vop.op === void 0) {
|
|
4126
|
-
throw new Error("VERTICAL_OPERATION: at least one op is required (set op on the block or on individual ROWs)");
|
|
4127
|
-
}
|
|
4128
|
-
let cols = 1;
|
|
4129
|
-
for (const item of vop.items) {
|
|
4130
|
-
if (item.type === AST_TYPES.VOP_ROW || item.type === AST_TYPES.VOP_CARRY_ROW) {
|
|
4131
|
-
cols = Math.max(cols, item.cells.length);
|
|
4132
|
-
}
|
|
4133
|
-
}
|
|
4134
|
-
function buildCells(row) {
|
|
4135
|
-
return row.cells.map((cell) => {
|
|
4136
|
-
const id = idGenerator.generate(JSON_TYPES.VOP_CELL);
|
|
4137
|
-
if (cell.kind === "digit") return { id, type: JSON_TYPES.VOP_CELL, kind: "digit", value: cell.value };
|
|
4138
|
-
if (cell.kind === "label") return { id, type: JSON_TYPES.VOP_CELL, kind: "label", value: cell.value };
|
|
4139
|
-
if (cell.kind === "color") return { id, type: JSON_TYPES.VOP_CELL, kind: "color", value: cell.value, color: cell.color };
|
|
4140
|
-
if (cell.kind === "bold") return { id, type: JSON_TYPES.VOP_CELL, kind: "bold", value: cell.value };
|
|
4141
|
-
if (cell.kind === "highlight") return { id, type: JSON_TYPES.VOP_CELL, kind: "highlight", value: cell.value };
|
|
4142
|
-
if (cell.kind === "input") return { id, type: JSON_TYPES.VOP_CELL, kind: "input", widgetId: cell.name };
|
|
4143
|
-
return { id, type: JSON_TYPES.VOP_CELL, kind: "empty" };
|
|
4144
|
-
});
|
|
4145
|
-
}
|
|
4146
|
-
const items = vop.items.map((item) => {
|
|
4147
|
-
if (item.type === AST_TYPES.VOP_SEPARATOR) {
|
|
4148
|
-
return {
|
|
4149
|
-
id: idGenerator.generate(JSON_TYPES.VOP_SEPARATOR),
|
|
4150
|
-
type: JSON_TYPES.VOP_SEPARATOR
|
|
4151
|
-
};
|
|
4152
|
-
}
|
|
4153
|
-
if (item.type === AST_TYPES.VOP_CARRY_ROW) {
|
|
4154
|
-
return {
|
|
4155
|
-
id: idGenerator.generate(JSON_TYPES.VOP_CARRY_ROW),
|
|
4156
|
-
type: JSON_TYPES.VOP_CARRY_ROW,
|
|
4157
|
-
cells: buildCells(item)
|
|
4158
|
-
};
|
|
4159
|
-
}
|
|
4160
|
-
const row = item;
|
|
4161
|
-
return {
|
|
4162
|
-
id: idGenerator.generate(JSON_TYPES.VOP_ROW),
|
|
4163
|
-
type: JSON_TYPES.VOP_ROW,
|
|
4164
|
-
cells: buildCells(row),
|
|
4165
|
-
...row.op !== void 0 && { op: row.op }
|
|
4166
|
-
};
|
|
4167
|
-
});
|
|
4168
|
-
return {
|
|
4169
|
-
id: idGenerator.generate(JSON_TYPES.VERTICAL_OPERATION),
|
|
4170
|
-
type: JSON_TYPES.VERTICAL_OPERATION,
|
|
4171
|
-
op: vop.op,
|
|
4172
|
-
cols,
|
|
4173
|
-
items
|
|
4174
|
-
};
|
|
4175
|
-
}
|
|
4176
|
-
function transformTable(table, idGenerator, inlineDragIds) {
|
|
4177
|
-
const rows = table.rows.map((row) => {
|
|
4178
|
-
const cells = row.cells.map((cell) => {
|
|
4179
|
-
const items = cell.inlines.map((inline) => transformInline(inline, idGenerator, inlineDragIds));
|
|
4180
|
-
const cellItem = {
|
|
4181
|
-
id: idGenerator.generate(JSON_TYPES.TABLE_CELL),
|
|
4182
|
-
type: JSON_TYPES.TABLE_CELL,
|
|
4183
|
-
items
|
|
4184
|
-
};
|
|
4185
|
-
if (cell.colspan !== void 0) {
|
|
4186
|
-
cellItem.colspan = cell.colspan;
|
|
4187
|
-
}
|
|
4188
|
-
return cellItem;
|
|
4189
|
-
});
|
|
4190
|
-
return {
|
|
4191
|
-
id: idGenerator.generate(JSON_TYPES.TABLE_ROW),
|
|
4192
|
-
type: JSON_TYPES.TABLE_ROW,
|
|
4193
|
-
cells
|
|
4194
|
-
};
|
|
4195
|
-
});
|
|
4196
|
-
return {
|
|
4197
|
-
id: idGenerator.generate(JSON_TYPES.TABLE),
|
|
4198
|
-
type: JSON_TYPES.TABLE,
|
|
4199
|
-
cols: table.cols,
|
|
4200
|
-
rows
|
|
4201
|
-
};
|
|
4202
|
-
}
|
|
4203
|
-
function transformLine(line, idGenerator, inlineDragIds) {
|
|
4204
|
-
const items = [];
|
|
4205
|
-
for (const inline of line.inlines) {
|
|
4206
|
-
items.push(transformInline(inline, idGenerator, inlineDragIds));
|
|
4207
|
-
}
|
|
4208
|
-
items.push({
|
|
4209
|
-
id: idGenerator.generate(JSON_TYPES.LINEBREAK),
|
|
4210
|
-
type: JSON_TYPES.LINEBREAK
|
|
4211
|
-
});
|
|
4212
|
-
return items;
|
|
4213
|
-
}
|
|
4214
|
-
function transformInline(inline, idGenerator, inlineDragIds) {
|
|
4215
|
-
switch (inline.type) {
|
|
4216
|
-
case AST_TYPES.FREE_TEXT: {
|
|
4217
|
-
const trimmed = inline.value.trim();
|
|
4218
|
-
const operators = ["+", "-", "×", "÷", "=", "<", ">", "?"];
|
|
4219
|
-
if (operators.includes(trimmed) && inline.value.trim() === inline.value) {
|
|
4220
|
-
return {
|
|
4221
|
-
id: idGenerator.generate("operator"),
|
|
4222
|
-
type: "operator",
|
|
4223
|
-
operator: trimmed
|
|
4224
|
-
};
|
|
4225
|
-
}
|
|
4226
|
-
return {
|
|
4227
|
-
id: idGenerator.generate(JSON_TYPES.TEXT),
|
|
4228
|
-
type: JSON_TYPES.TEXT,
|
|
4229
|
-
value: inline.value
|
|
4230
|
-
};
|
|
4231
|
-
}
|
|
4232
|
-
case AST_TYPES.SPACE:
|
|
4233
|
-
return {
|
|
4234
|
-
id: idGenerator.generate(JSON_TYPES.SPACE),
|
|
4235
|
-
type: JSON_TYPES.SPACE
|
|
4236
|
-
};
|
|
4237
|
-
case AST_TYPES.HIGHLIGHTED_TEXT:
|
|
4238
|
-
return {
|
|
4239
|
-
id: idGenerator.generate(JSON_TYPES.HIGHLIGHTED_TEXT),
|
|
4240
|
-
type: JSON_TYPES.HIGHLIGHTED_TEXT,
|
|
4241
|
-
value: inline.value,
|
|
4242
|
-
highlight: inline.highlight
|
|
4243
|
-
};
|
|
4244
|
-
case AST_TYPES.COLOR_TEXT:
|
|
4245
|
-
return {
|
|
4246
|
-
id: idGenerator.generate(JSON_TYPES.COLOR_TEXT),
|
|
4247
|
-
type: JSON_TYPES.COLOR_TEXT,
|
|
4248
|
-
value: inline.value,
|
|
4249
|
-
color: inline.color,
|
|
4250
|
-
highlight: inline.highlight
|
|
4251
|
-
};
|
|
4252
|
-
case AST_TYPES.BOLD_TEXT:
|
|
4253
|
-
return {
|
|
4254
|
-
id: idGenerator.generate(JSON_TYPES.BOLD_TEXT),
|
|
4255
|
-
type: JSON_TYPES.BOLD_TEXT,
|
|
4256
|
-
value: inline.value,
|
|
4257
|
-
highlight: inline.highlight
|
|
4258
|
-
};
|
|
4259
|
-
case AST_TYPES.INLINE_INPUT:
|
|
4260
|
-
return {
|
|
4261
|
-
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
4262
|
-
type: JSON_TYPES.WIDGET_REF,
|
|
4263
|
-
widgetId: inline.name
|
|
4264
|
-
};
|
|
4265
|
-
case AST_TYPES.FRACTION:
|
|
4266
|
-
return {
|
|
4267
|
-
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
4268
|
-
type: JSON_TYPES.WIDGET_REF,
|
|
4269
|
-
widgetId: inline.name
|
|
4270
|
-
};
|
|
4271
|
-
case AST_TYPES.IMAGE:
|
|
4272
|
-
return {
|
|
4273
|
-
id: idGenerator.generate(JSON_TYPES.IMAGE),
|
|
4274
|
-
type: JSON_TYPES.IMAGE,
|
|
4275
|
-
name: inline.name,
|
|
4276
|
-
src: inline.src,
|
|
4277
|
-
alt: inline.alt,
|
|
4278
|
-
...inline.width !== void 0 && { width: inline.width },
|
|
4279
|
-
...inline.height !== void 0 && { height: inline.height }
|
|
4280
|
-
};
|
|
4281
|
-
case AST_TYPES.VISUAL_FRACTION:
|
|
4282
|
-
return {
|
|
4283
|
-
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
4284
|
-
type: JSON_TYPES.WIDGET_REF,
|
|
4285
|
-
widgetId: inline.name
|
|
4286
|
-
};
|
|
4287
|
-
case AST_TYPES.INLINE_DRAG_OPTION:
|
|
4288
|
-
return {
|
|
4289
|
-
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
4290
|
-
type: JSON_TYPES.WIDGET_REF,
|
|
4291
|
-
widgetId: inlineDragIds.get(inline)
|
|
4292
|
-
};
|
|
4293
|
-
case AST_TYPES.CLOCK: {
|
|
4294
|
-
const base = {
|
|
4295
|
-
id: idGenerator.generate(JSON_TYPES.CLOCK),
|
|
4296
|
-
type: JSON_TYPES.CLOCK,
|
|
4297
|
-
mode: inline.mode,
|
|
4298
|
-
clockType: inline.clockType,
|
|
4299
|
-
value: inline.value,
|
|
4300
|
-
...inline.format !== void 0 && { format: inline.format },
|
|
4301
|
-
...inline.name !== void 0 && { name: inline.name }
|
|
4302
|
-
};
|
|
4303
|
-
if (inline.mode === "interactive") {
|
|
4304
|
-
return { ...base, ...inline.snap !== void 0 && { snap: inline.snap } };
|
|
4305
|
-
}
|
|
4306
|
-
return base;
|
|
4307
|
-
}
|
|
4308
|
-
default:
|
|
4309
|
-
throw new Error(`Unknown inline type: ${inline.type}`);
|
|
4310
|
-
}
|
|
4311
|
-
}
|
|
4312
|
-
function transformValidation(validation, answers, widgets) {
|
|
4313
|
-
if (validation.type === AST_TYPES.SIMPLE_VALIDATION) {
|
|
4314
|
-
return {
|
|
4315
|
-
mode: VALIDATION_MODES.SIMPLE,
|
|
4316
|
-
answers
|
|
4317
|
-
};
|
|
4318
|
-
} else if (validation.type === AST_TYPES.EXPRESSION_VALIDATION) {
|
|
4319
|
-
const validVariableNames = Object.keys(widgets);
|
|
4320
|
-
const variables = extractVariables$1(validation.expression, validVariableNames);
|
|
4321
|
-
return {
|
|
4322
|
-
mode: VALIDATION_MODES.EXPRESSION,
|
|
4323
|
-
expression: validation.expression,
|
|
4324
|
-
variables
|
|
4325
|
-
};
|
|
4326
|
-
} else if (validation.type === AST_TYPES.MIXED_VALIDATION) {
|
|
4327
|
-
const validVariableNames = Object.keys(widgets);
|
|
4328
|
-
const variables = extractVariables$1(validation.expression, validVariableNames);
|
|
4329
|
-
const inExpression = new Set(variables);
|
|
4330
|
-
for (const [name, widget] of Object.entries(widgets)) {
|
|
4331
|
-
if (name.startsWith("_")) continue;
|
|
4332
|
-
if (name in answers) continue;
|
|
4333
|
-
if (inExpression.has(name)) continue;
|
|
4334
|
-
if (widget.type === JSON_TYPES.FRACTION) {
|
|
4335
|
-
const w = widget;
|
|
4336
|
-
if (w.numerator !== null && w.denominator !== null) continue;
|
|
4337
|
-
}
|
|
4338
|
-
if (widget.type === JSON_TYPES.VISUAL_FRACTION) {
|
|
4339
|
-
const w = widget;
|
|
4340
|
-
if (Array.isArray(w.filled) && w.filled.length > 0) continue;
|
|
4341
|
-
}
|
|
4342
|
-
throw new Error(
|
|
4343
|
-
`Mixed validation: widget "${name}" has no \`answer\` and is not referenced in expression "${validation.expression}"`
|
|
4344
|
-
);
|
|
4345
|
-
}
|
|
4346
|
-
return {
|
|
4347
|
-
mode: VALIDATION_MODES.MIXED,
|
|
4348
|
-
expression: validation.expression,
|
|
4349
|
-
answers,
|
|
4350
|
-
variables
|
|
4351
|
-
};
|
|
4352
|
-
}
|
|
4353
|
-
throw new Error(`Unknown validation type: ${validation.type}`);
|
|
4354
|
-
}
|
|
4355
|
-
function extractVariables$1(expression, validVariables) {
|
|
4356
|
-
const matches = expression.match(/\b[a-zA-Z_][a-zA-Z0-9_]*\b/g) || [];
|
|
4357
|
-
const variables = matches.filter(
|
|
4358
|
-
(v) => v !== "true" && v !== "false" && v !== "null" && v !== "undefined"
|
|
4359
|
-
);
|
|
4360
|
-
const uniqueVars = Array.from(new Set(variables));
|
|
4361
|
-
if (validVariables && validVariables.length > 0) {
|
|
4362
|
-
return uniqueVars.filter((v) => validVariables.includes(v));
|
|
4363
|
-
}
|
|
4364
|
-
return uniqueVars;
|
|
4365
|
-
}
|
|
4366
|
-
function normalize(value) {
|
|
4367
|
-
if (typeof value === "number") {
|
|
4368
|
-
return String(Math.round(value * 10 ** INTERNAL_PRECISION) / 10 ** INTERNAL_PRECISION);
|
|
4369
|
-
}
|
|
4370
|
-
const trimmed = String(value).trim().toLowerCase().replace(/^["']|["']$/g, "");
|
|
4371
|
-
const asNum = Number(trimmed);
|
|
4372
|
-
if (trimmed !== "" && !isNaN(asNum)) {
|
|
4373
|
-
return String(Math.round(asNum * 10 ** INTERNAL_PRECISION) / 10 ** INTERNAL_PRECISION);
|
|
4374
|
-
}
|
|
4375
|
-
return trimmed;
|
|
4376
|
-
}
|
|
4377
|
-
const NL_DRAG_PREFIX = "\0nl-drag\0";
|
|
4378
|
-
const isNLDragEncoded = (v) => v.startsWith(NL_DRAG_PREFIX);
|
|
4379
|
-
const encodeNLDragValue = (label, position) => `${NL_DRAG_PREFIX}${label}@${String(position)}`;
|
|
4380
|
-
function isNLDragCorrect(encoded, expectedLabel, expectedPosition) {
|
|
4381
|
-
const payload = encoded.slice(NL_DRAG_PREFIX.length);
|
|
4382
|
-
const atIdx = payload.lastIndexOf("@");
|
|
4383
|
-
const label = normalize(payload.slice(0, atIdx));
|
|
4384
|
-
const position = normalize(payload.slice(atIdx + 1));
|
|
4385
|
-
return label === normalize(expectedLabel) && position === normalize(expectedPosition);
|
|
4386
|
-
}
|
|
4387
3758
|
var commonjsGlobal = typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : {};
|
|
4388
3759
|
var lodash = { exports: {} };
|
|
4389
3760
|
/**
|
|
@@ -9701,172 +9072,803 @@ lodash.exports;
|
|
|
9701
9072
|
arrayEach(["bind", "bindKey", "curry", "curryRight", "partial", "partialRight"], function(methodName) {
|
|
9702
9073
|
lodash2[methodName].placeholder = lodash2;
|
|
9703
9074
|
});
|
|
9704
|
-
arrayEach(["drop", "take"], function(methodName, index) {
|
|
9705
|
-
LazyWrapper.prototype[methodName] = function(n) {
|
|
9706
|
-
n = n === undefined$1 ? 1 : nativeMax(toInteger(n), 0);
|
|
9707
|
-
var result2 = this.__filtered__ && !index ? new LazyWrapper(this) : this.clone();
|
|
9708
|
-
if (result2.__filtered__) {
|
|
9709
|
-
result2.__takeCount__ = nativeMin(n, result2.__takeCount__);
|
|
9710
|
-
} else {
|
|
9711
|
-
result2.__views__.push({
|
|
9712
|
-
"size": nativeMin(n, MAX_ARRAY_LENGTH),
|
|
9713
|
-
"type": methodName + (result2.__dir__ < 0 ? "Right" : "")
|
|
9714
|
-
});
|
|
9715
|
-
}
|
|
9716
|
-
return result2;
|
|
9075
|
+
arrayEach(["drop", "take"], function(methodName, index) {
|
|
9076
|
+
LazyWrapper.prototype[methodName] = function(n) {
|
|
9077
|
+
n = n === undefined$1 ? 1 : nativeMax(toInteger(n), 0);
|
|
9078
|
+
var result2 = this.__filtered__ && !index ? new LazyWrapper(this) : this.clone();
|
|
9079
|
+
if (result2.__filtered__) {
|
|
9080
|
+
result2.__takeCount__ = nativeMin(n, result2.__takeCount__);
|
|
9081
|
+
} else {
|
|
9082
|
+
result2.__views__.push({
|
|
9083
|
+
"size": nativeMin(n, MAX_ARRAY_LENGTH),
|
|
9084
|
+
"type": methodName + (result2.__dir__ < 0 ? "Right" : "")
|
|
9085
|
+
});
|
|
9086
|
+
}
|
|
9087
|
+
return result2;
|
|
9088
|
+
};
|
|
9089
|
+
LazyWrapper.prototype[methodName + "Right"] = function(n) {
|
|
9090
|
+
return this.reverse()[methodName](n).reverse();
|
|
9091
|
+
};
|
|
9092
|
+
});
|
|
9093
|
+
arrayEach(["filter", "map", "takeWhile"], function(methodName, index) {
|
|
9094
|
+
var type = index + 1, isFilter = type == LAZY_FILTER_FLAG || type == LAZY_WHILE_FLAG;
|
|
9095
|
+
LazyWrapper.prototype[methodName] = function(iteratee2) {
|
|
9096
|
+
var result2 = this.clone();
|
|
9097
|
+
result2.__iteratees__.push({
|
|
9098
|
+
"iteratee": getIteratee(iteratee2, 3),
|
|
9099
|
+
"type": type
|
|
9100
|
+
});
|
|
9101
|
+
result2.__filtered__ = result2.__filtered__ || isFilter;
|
|
9102
|
+
return result2;
|
|
9103
|
+
};
|
|
9104
|
+
});
|
|
9105
|
+
arrayEach(["head", "last"], function(methodName, index) {
|
|
9106
|
+
var takeName = "take" + (index ? "Right" : "");
|
|
9107
|
+
LazyWrapper.prototype[methodName] = function() {
|
|
9108
|
+
return this[takeName](1).value()[0];
|
|
9109
|
+
};
|
|
9110
|
+
});
|
|
9111
|
+
arrayEach(["initial", "tail"], function(methodName, index) {
|
|
9112
|
+
var dropName = "drop" + (index ? "" : "Right");
|
|
9113
|
+
LazyWrapper.prototype[methodName] = function() {
|
|
9114
|
+
return this.__filtered__ ? new LazyWrapper(this) : this[dropName](1);
|
|
9115
|
+
};
|
|
9116
|
+
});
|
|
9117
|
+
LazyWrapper.prototype.compact = function() {
|
|
9118
|
+
return this.filter(identity);
|
|
9119
|
+
};
|
|
9120
|
+
LazyWrapper.prototype.find = function(predicate) {
|
|
9121
|
+
return this.filter(predicate).head();
|
|
9122
|
+
};
|
|
9123
|
+
LazyWrapper.prototype.findLast = function(predicate) {
|
|
9124
|
+
return this.reverse().find(predicate);
|
|
9125
|
+
};
|
|
9126
|
+
LazyWrapper.prototype.invokeMap = baseRest(function(path, args) {
|
|
9127
|
+
if (typeof path == "function") {
|
|
9128
|
+
return new LazyWrapper(this);
|
|
9129
|
+
}
|
|
9130
|
+
return this.map(function(value) {
|
|
9131
|
+
return baseInvoke(value, path, args);
|
|
9132
|
+
});
|
|
9133
|
+
});
|
|
9134
|
+
LazyWrapper.prototype.reject = function(predicate) {
|
|
9135
|
+
return this.filter(negate(getIteratee(predicate)));
|
|
9136
|
+
};
|
|
9137
|
+
LazyWrapper.prototype.slice = function(start, end) {
|
|
9138
|
+
start = toInteger(start);
|
|
9139
|
+
var result2 = this;
|
|
9140
|
+
if (result2.__filtered__ && (start > 0 || end < 0)) {
|
|
9141
|
+
return new LazyWrapper(result2);
|
|
9142
|
+
}
|
|
9143
|
+
if (start < 0) {
|
|
9144
|
+
result2 = result2.takeRight(-start);
|
|
9145
|
+
} else if (start) {
|
|
9146
|
+
result2 = result2.drop(start);
|
|
9147
|
+
}
|
|
9148
|
+
if (end !== undefined$1) {
|
|
9149
|
+
end = toInteger(end);
|
|
9150
|
+
result2 = end < 0 ? result2.dropRight(-end) : result2.take(end - start);
|
|
9151
|
+
}
|
|
9152
|
+
return result2;
|
|
9153
|
+
};
|
|
9154
|
+
LazyWrapper.prototype.takeRightWhile = function(predicate) {
|
|
9155
|
+
return this.reverse().takeWhile(predicate).reverse();
|
|
9156
|
+
};
|
|
9157
|
+
LazyWrapper.prototype.toArray = function() {
|
|
9158
|
+
return this.take(MAX_ARRAY_LENGTH);
|
|
9159
|
+
};
|
|
9160
|
+
baseForOwn(LazyWrapper.prototype, function(func, methodName) {
|
|
9161
|
+
var checkIteratee = /^(?:filter|find|map|reject)|While$/.test(methodName), isTaker = /^(?:head|last)$/.test(methodName), lodashFunc = lodash2[isTaker ? "take" + (methodName == "last" ? "Right" : "") : methodName], retUnwrapped = isTaker || /^find/.test(methodName);
|
|
9162
|
+
if (!lodashFunc) {
|
|
9163
|
+
return;
|
|
9164
|
+
}
|
|
9165
|
+
lodash2.prototype[methodName] = function() {
|
|
9166
|
+
var value = this.__wrapped__, args = isTaker ? [1] : arguments, isLazy = value instanceof LazyWrapper, iteratee2 = args[0], useLazy = isLazy || isArray(value);
|
|
9167
|
+
var interceptor = function(value2) {
|
|
9168
|
+
var result3 = lodashFunc.apply(lodash2, arrayPush([value2], args));
|
|
9169
|
+
return isTaker && chainAll ? result3[0] : result3;
|
|
9170
|
+
};
|
|
9171
|
+
if (useLazy && checkIteratee && typeof iteratee2 == "function" && iteratee2.length != 1) {
|
|
9172
|
+
isLazy = useLazy = false;
|
|
9173
|
+
}
|
|
9174
|
+
var chainAll = this.__chain__, isHybrid = !!this.__actions__.length, isUnwrapped = retUnwrapped && !chainAll, onlyLazy = isLazy && !isHybrid;
|
|
9175
|
+
if (!retUnwrapped && useLazy) {
|
|
9176
|
+
value = onlyLazy ? value : new LazyWrapper(this);
|
|
9177
|
+
var result2 = func.apply(value, args);
|
|
9178
|
+
result2.__actions__.push({ "func": thru, "args": [interceptor], "thisArg": undefined$1 });
|
|
9179
|
+
return new LodashWrapper(result2, chainAll);
|
|
9180
|
+
}
|
|
9181
|
+
if (isUnwrapped && onlyLazy) {
|
|
9182
|
+
return func.apply(this, args);
|
|
9183
|
+
}
|
|
9184
|
+
result2 = this.thru(interceptor);
|
|
9185
|
+
return isUnwrapped ? isTaker ? result2.value()[0] : result2.value() : result2;
|
|
9186
|
+
};
|
|
9187
|
+
});
|
|
9188
|
+
arrayEach(["pop", "push", "shift", "sort", "splice", "unshift"], function(methodName) {
|
|
9189
|
+
var func = arrayProto[methodName], chainName = /^(?:push|sort|unshift)$/.test(methodName) ? "tap" : "thru", retUnwrapped = /^(?:pop|shift)$/.test(methodName);
|
|
9190
|
+
lodash2.prototype[methodName] = function() {
|
|
9191
|
+
var args = arguments;
|
|
9192
|
+
if (retUnwrapped && !this.__chain__) {
|
|
9193
|
+
var value = this.value();
|
|
9194
|
+
return func.apply(isArray(value) ? value : [], args);
|
|
9195
|
+
}
|
|
9196
|
+
return this[chainName](function(value2) {
|
|
9197
|
+
return func.apply(isArray(value2) ? value2 : [], args);
|
|
9198
|
+
});
|
|
9199
|
+
};
|
|
9200
|
+
});
|
|
9201
|
+
baseForOwn(LazyWrapper.prototype, function(func, methodName) {
|
|
9202
|
+
var lodashFunc = lodash2[methodName];
|
|
9203
|
+
if (lodashFunc) {
|
|
9204
|
+
var key2 = lodashFunc.name + "";
|
|
9205
|
+
if (!hasOwnProperty.call(realNames, key2)) {
|
|
9206
|
+
realNames[key2] = [];
|
|
9207
|
+
}
|
|
9208
|
+
realNames[key2].push({ "name": methodName, "func": lodashFunc });
|
|
9209
|
+
}
|
|
9210
|
+
});
|
|
9211
|
+
realNames[createHybrid(undefined$1, WRAP_BIND_KEY_FLAG).name] = [{
|
|
9212
|
+
"name": "wrapper",
|
|
9213
|
+
"func": undefined$1
|
|
9214
|
+
}];
|
|
9215
|
+
LazyWrapper.prototype.clone = lazyClone;
|
|
9216
|
+
LazyWrapper.prototype.reverse = lazyReverse;
|
|
9217
|
+
LazyWrapper.prototype.value = lazyValue;
|
|
9218
|
+
lodash2.prototype.at = wrapperAt;
|
|
9219
|
+
lodash2.prototype.chain = wrapperChain;
|
|
9220
|
+
lodash2.prototype.commit = wrapperCommit;
|
|
9221
|
+
lodash2.prototype.next = wrapperNext;
|
|
9222
|
+
lodash2.prototype.plant = wrapperPlant;
|
|
9223
|
+
lodash2.prototype.reverse = wrapperReverse;
|
|
9224
|
+
lodash2.prototype.toJSON = lodash2.prototype.valueOf = lodash2.prototype.value = wrapperValue;
|
|
9225
|
+
lodash2.prototype.first = lodash2.prototype.head;
|
|
9226
|
+
if (symIterator) {
|
|
9227
|
+
lodash2.prototype[symIterator] = wrapperToIterator;
|
|
9228
|
+
}
|
|
9229
|
+
return lodash2;
|
|
9230
|
+
};
|
|
9231
|
+
var _ = runInContext();
|
|
9232
|
+
if (freeModule) {
|
|
9233
|
+
(freeModule.exports = _)._ = _;
|
|
9234
|
+
freeExports._ = _;
|
|
9235
|
+
} else {
|
|
9236
|
+
root._ = _;
|
|
9237
|
+
}
|
|
9238
|
+
}).call(commonjsGlobal);
|
|
9239
|
+
})(lodash, lodash.exports);
|
|
9240
|
+
var lodashExports = lodash.exports;
|
|
9241
|
+
class LayoutIdGenerator {
|
|
9242
|
+
constructor() {
|
|
9243
|
+
this.counter = 0;
|
|
9244
|
+
}
|
|
9245
|
+
reset() {
|
|
9246
|
+
this.counter = 0;
|
|
9247
|
+
}
|
|
9248
|
+
generate(type) {
|
|
9249
|
+
return `${type}_${++this.counter}`;
|
|
9250
|
+
}
|
|
9251
|
+
}
|
|
9252
|
+
function toJSON(ast) {
|
|
9253
|
+
const idGenerator = new LayoutIdGenerator();
|
|
9254
|
+
const widgets = {};
|
|
9255
|
+
const answers = {};
|
|
9256
|
+
const allInlines = collectAllInlines(ast.lines);
|
|
9257
|
+
const inlineDragIds = /* @__PURE__ */ new Map();
|
|
9258
|
+
{
|
|
9259
|
+
let counter = 0;
|
|
9260
|
+
for (const inline of allInlines) {
|
|
9261
|
+
if (inline.type === AST_TYPES.INLINE_DRAG_OPTION) {
|
|
9262
|
+
inlineDragIds.set(inline, `${INLINE_DRAG_OPT_PREFIX}${counter++}`);
|
|
9263
|
+
}
|
|
9264
|
+
}
|
|
9265
|
+
}
|
|
9266
|
+
const layout = [];
|
|
9267
|
+
if (ast.question) {
|
|
9268
|
+
layout.push({
|
|
9269
|
+
id: idGenerator.generate(JSON_TYPES.QUESTION_TEXT),
|
|
9270
|
+
type: JSON_TYPES.QUESTION_TEXT,
|
|
9271
|
+
value: ast.question.value
|
|
9272
|
+
});
|
|
9273
|
+
}
|
|
9274
|
+
for (const block of ast.lines) {
|
|
9275
|
+
if (block.type === AST_TYPES.TABLE_BLOCK) {
|
|
9276
|
+
layout.push(transformTable(block, idGenerator, inlineDragIds));
|
|
9277
|
+
} else if (block.type === AST_TYPES.NUMBER_LINE_BLOCK) {
|
|
9278
|
+
const nlItem = transformNumberLine(block, idGenerator, inlineDragIds);
|
|
9279
|
+
layout.push(nlItem);
|
|
9280
|
+
if (nlItem.counter?.answer !== void 0) {
|
|
9281
|
+
answers[nlItem.counter.id] = nlItem.counter.answer;
|
|
9282
|
+
}
|
|
9283
|
+
for (const cursor of nlItem.cursors ?? []) {
|
|
9284
|
+
answers[cursor.id] = cursor.answer;
|
|
9285
|
+
}
|
|
9286
|
+
} else if (block.type === AST_TYPES.MATCHER_BLOCK) {
|
|
9287
|
+
const matcherBlock = block;
|
|
9288
|
+
layout.push({
|
|
9289
|
+
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
9290
|
+
type: JSON_TYPES.WIDGET_REF,
|
|
9291
|
+
widgetId: matcherBlock.name
|
|
9292
|
+
});
|
|
9293
|
+
layout.push({
|
|
9294
|
+
id: idGenerator.generate(JSON_TYPES.LINEBREAK),
|
|
9295
|
+
type: JSON_TYPES.LINEBREAK
|
|
9296
|
+
});
|
|
9297
|
+
} else if (block.type === AST_TYPES.PAIR_MATCHER_BLOCK) {
|
|
9298
|
+
const pmBlock = block;
|
|
9299
|
+
layout.push({
|
|
9300
|
+
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
9301
|
+
type: JSON_TYPES.WIDGET_REF,
|
|
9302
|
+
widgetId: pmBlock.name
|
|
9303
|
+
});
|
|
9304
|
+
layout.push({
|
|
9305
|
+
id: idGenerator.generate(JSON_TYPES.LINEBREAK),
|
|
9306
|
+
type: JSON_TYPES.LINEBREAK
|
|
9307
|
+
});
|
|
9308
|
+
} else if (block.type === AST_TYPES.VERTICAL_OPERATION_BLOCK) {
|
|
9309
|
+
layout.push(transformVerticalOperation(block, idGenerator));
|
|
9310
|
+
} else if (block.type === AST_TYPES.RADIO_OPTIONS) {
|
|
9311
|
+
const radioBlock = block;
|
|
9312
|
+
layout.push({
|
|
9313
|
+
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
9314
|
+
type: JSON_TYPES.WIDGET_REF,
|
|
9315
|
+
widgetId: radioBlock.name
|
|
9316
|
+
});
|
|
9317
|
+
layout.push({
|
|
9318
|
+
id: idGenerator.generate(JSON_TYPES.LINEBREAK),
|
|
9319
|
+
type: JSON_TYPES.LINEBREAK
|
|
9320
|
+
});
|
|
9321
|
+
} else {
|
|
9322
|
+
const lineItems = transformLine(block, idGenerator, inlineDragIds);
|
|
9323
|
+
layout.push(...lineItems);
|
|
9324
|
+
}
|
|
9325
|
+
}
|
|
9326
|
+
for (const inline of allInlines) {
|
|
9327
|
+
if (inline.type === AST_TYPES.INLINE_INPUT) {
|
|
9328
|
+
if (inline.inputType === INPUT_TYPES.STRING) {
|
|
9329
|
+
widgets[inline.name] = {
|
|
9330
|
+
type: JSON_TYPES.INLINE_INPUT,
|
|
9331
|
+
inputType: INPUT_TYPES.STRING,
|
|
9332
|
+
length: inline.length,
|
|
9333
|
+
placeholder: inline.placeholder
|
|
9717
9334
|
};
|
|
9718
|
-
|
|
9719
|
-
|
|
9335
|
+
} else {
|
|
9336
|
+
widgets[inline.name] = {
|
|
9337
|
+
type: JSON_TYPES.INLINE_INPUT,
|
|
9338
|
+
inputType: INPUT_TYPES.NUMERIC,
|
|
9339
|
+
digits: inline.digits,
|
|
9340
|
+
placeholder: inline.placeholder
|
|
9720
9341
|
};
|
|
9721
|
-
}
|
|
9722
|
-
|
|
9723
|
-
|
|
9724
|
-
|
|
9725
|
-
|
|
9726
|
-
|
|
9727
|
-
|
|
9728
|
-
|
|
9729
|
-
|
|
9730
|
-
|
|
9731
|
-
|
|
9342
|
+
}
|
|
9343
|
+
if (inline.answer !== void 0) {
|
|
9344
|
+
answers[inline.name] = inline.answer;
|
|
9345
|
+
}
|
|
9346
|
+
} else if (inline.type === AST_TYPES.CLOCK && inline.mode === "interactive") {
|
|
9347
|
+
answers[inline.name] = inline.answer;
|
|
9348
|
+
}
|
|
9349
|
+
if (inline.type === AST_TYPES.FRACTION) {
|
|
9350
|
+
widgets[inline.name] = {
|
|
9351
|
+
type: JSON_TYPES.FRACTION,
|
|
9352
|
+
numerator: inline.numerator ?? null,
|
|
9353
|
+
denominator: inline.denominator ?? null,
|
|
9354
|
+
simplified: inline.simplified ?? true,
|
|
9355
|
+
placeholder: inline.placeholder
|
|
9356
|
+
};
|
|
9357
|
+
if (inline.answer !== void 0) {
|
|
9358
|
+
answers[inline.name] = inline.answer;
|
|
9359
|
+
}
|
|
9360
|
+
}
|
|
9361
|
+
if (inline.type === AST_TYPES.INLINE_DRAG_OPTION) {
|
|
9362
|
+
const widgetId = inlineDragIds.get(inline);
|
|
9363
|
+
widgets[widgetId] = {
|
|
9364
|
+
type: JSON_TYPES.INLINE_DRAG_OPTION,
|
|
9365
|
+
value: inline.value
|
|
9366
|
+
};
|
|
9367
|
+
}
|
|
9368
|
+
if (inline.type === AST_TYPES.VISUAL_FRACTION) {
|
|
9369
|
+
const answerArr = inline.answer ?? [];
|
|
9370
|
+
widgets[inline.name] = {
|
|
9371
|
+
type: JSON_TYPES.VISUAL_FRACTION,
|
|
9372
|
+
shape: inline.shape,
|
|
9373
|
+
divisions: inline.divisions,
|
|
9374
|
+
filled: inline.filled ?? [],
|
|
9375
|
+
answer: answerArr,
|
|
9376
|
+
...inline.orientation !== void 0 && { orientation: inline.orientation },
|
|
9377
|
+
...inline.label !== void 0 && { label: inline.label },
|
|
9378
|
+
...inline.size !== void 0 && { size: inline.size }
|
|
9379
|
+
};
|
|
9380
|
+
if (answerArr.length > 0) {
|
|
9381
|
+
answers[inline.name] = answerArr;
|
|
9382
|
+
}
|
|
9383
|
+
}
|
|
9384
|
+
}
|
|
9385
|
+
for (const block of ast.lines) {
|
|
9386
|
+
if (block.type !== AST_TYPES.VERTICAL_OPERATION_BLOCK) continue;
|
|
9387
|
+
const vop = block;
|
|
9388
|
+
for (const item of vop.items) {
|
|
9389
|
+
if (item.type !== AST_TYPES.VOP_ROW && item.type !== AST_TYPES.VOP_CARRY_ROW) continue;
|
|
9390
|
+
for (const cell of item.cells) {
|
|
9391
|
+
if (cell.kind !== "input") continue;
|
|
9392
|
+
if (widgets[cell.name]) {
|
|
9393
|
+
throw new Error(`VERTICAL_OPERATION: duplicate INPUT name "${cell.name}"`);
|
|
9394
|
+
}
|
|
9395
|
+
widgets[cell.name] = {
|
|
9396
|
+
type: JSON_TYPES.INLINE_INPUT,
|
|
9397
|
+
inputType: INPUT_TYPES.NUMERIC,
|
|
9398
|
+
digits: cell.digits
|
|
9732
9399
|
};
|
|
9733
|
-
|
|
9734
|
-
|
|
9735
|
-
|
|
9736
|
-
|
|
9737
|
-
|
|
9400
|
+
if (cell.answer !== void 0) {
|
|
9401
|
+
answers[cell.name] = cell.answer;
|
|
9402
|
+
}
|
|
9403
|
+
}
|
|
9404
|
+
}
|
|
9405
|
+
}
|
|
9406
|
+
for (const block of ast.lines) {
|
|
9407
|
+
if (block.type === AST_TYPES.RADIO_OPTIONS) {
|
|
9408
|
+
const radioBlock = block;
|
|
9409
|
+
widgets[radioBlock.name] = {
|
|
9410
|
+
type: JSON_TYPES.RADIO_OPTIONS,
|
|
9411
|
+
// Randomise option order per compile so the answer position isn't fixed.
|
|
9412
|
+
options: lodashExports.shuffle(radioBlock.options)
|
|
9413
|
+
};
|
|
9414
|
+
answers[radioBlock.name] = radioBlock.answer;
|
|
9415
|
+
} else if (block.type === AST_TYPES.MATCHER_BLOCK) {
|
|
9416
|
+
const matcherBlock = block;
|
|
9417
|
+
widgets[matcherBlock.name] = buildMatcherWidget(matcherBlock, idGenerator, inlineDragIds);
|
|
9418
|
+
answers[matcherBlock.name] = matcherBlock.answers;
|
|
9419
|
+
} else if (block.type === AST_TYPES.PAIR_MATCHER_BLOCK) {
|
|
9420
|
+
const pmBlock = block;
|
|
9421
|
+
widgets[pmBlock.name] = buildPairMatcherWidget(pmBlock, idGenerator, inlineDragIds);
|
|
9422
|
+
answers[pmBlock.name] = pmBlock.answers;
|
|
9423
|
+
}
|
|
9424
|
+
}
|
|
9425
|
+
if (ast.tools) {
|
|
9426
|
+
for (const tool of ast.tools) {
|
|
9427
|
+
if (tool.type === AST_TYPES.DRAG_OPTIONS) {
|
|
9428
|
+
widgets["_drag_options"] = {
|
|
9429
|
+
type: JSON_TYPES.DRAG_OPTIONS,
|
|
9430
|
+
// Randomise chip order per compile so the answer position isn't fixed.
|
|
9431
|
+
options: lodashExports.shuffle(tool.options)
|
|
9738
9432
|
};
|
|
9739
|
-
})
|
|
9740
|
-
|
|
9741
|
-
|
|
9742
|
-
|
|
9743
|
-
|
|
9433
|
+
} else if (tool.type === AST_TYPES.NUMERIC_KEYPAD) {
|
|
9434
|
+
widgets["_numeric_keypad"] = {
|
|
9435
|
+
type: JSON_TYPES.NUMERIC_KEYPAD,
|
|
9436
|
+
includeOperators: tool.includeOperators,
|
|
9437
|
+
includeDecimal: tool.includeDecimal,
|
|
9438
|
+
style: tool.style
|
|
9439
|
+
};
|
|
9440
|
+
} else if (tool.type === AST_TYPES.GEOMETRY_RULER) {
|
|
9441
|
+
widgets[RULER_WIDGET_ID] = {
|
|
9442
|
+
type: JSON_TYPES.RULER,
|
|
9443
|
+
size: tool.size
|
|
9444
|
+
};
|
|
9445
|
+
} else if (tool.type === AST_TYPES.GEOMETRY_PROTRACTOR) {
|
|
9446
|
+
widgets[PROTRACTOR_WIDGET_ID] = {
|
|
9447
|
+
type: JSON_TYPES.PROTRACTOR,
|
|
9448
|
+
size: tool.size
|
|
9449
|
+
};
|
|
9450
|
+
} else if (tool.type === AST_TYPES.GEOMETRY_SET_SQUARE) {
|
|
9451
|
+
widgets[SET_SQUARE_WIDGET_ID] = {
|
|
9452
|
+
type: JSON_TYPES.SET_SQUARE,
|
|
9453
|
+
size: tool.size
|
|
9454
|
+
};
|
|
9455
|
+
}
|
|
9456
|
+
}
|
|
9457
|
+
}
|
|
9458
|
+
return {
|
|
9459
|
+
version: "2.0",
|
|
9460
|
+
// Natural DSL version
|
|
9461
|
+
layout,
|
|
9462
|
+
widgets,
|
|
9463
|
+
...ast.validation ? { validation: transformValidation(ast.validation, answers, widgets) } : {}
|
|
9464
|
+
};
|
|
9465
|
+
}
|
|
9466
|
+
function collectAllInlines(blocks) {
|
|
9467
|
+
const result = [];
|
|
9468
|
+
for (const block of blocks) {
|
|
9469
|
+
if (block.type === AST_TYPES.LINE_BLOCK) {
|
|
9470
|
+
result.push(...block.inlines);
|
|
9471
|
+
} else if (block.type === AST_TYPES.TABLE_BLOCK) {
|
|
9472
|
+
for (const row of block.rows) {
|
|
9473
|
+
for (const cell of row.cells) {
|
|
9474
|
+
result.push(...cell.inlines);
|
|
9475
|
+
}
|
|
9476
|
+
}
|
|
9477
|
+
} else if (block.type === AST_TYPES.NUMBER_LINE_BLOCK) {
|
|
9478
|
+
const numberLine = block;
|
|
9479
|
+
for (const position of numberLine.positions) {
|
|
9480
|
+
result.push(...position.inlines);
|
|
9481
|
+
}
|
|
9482
|
+
for (const arrow of numberLine.arrows ?? []) {
|
|
9483
|
+
result.push(...arrow.inlines);
|
|
9484
|
+
}
|
|
9485
|
+
} else if (block.type === AST_TYPES.MATCHER_BLOCK) {
|
|
9486
|
+
for (const row of block.rows) {
|
|
9487
|
+
for (const item of row.items) {
|
|
9488
|
+
result.push(...item.inlines);
|
|
9489
|
+
}
|
|
9490
|
+
}
|
|
9491
|
+
} else if (block.type === AST_TYPES.PAIR_MATCHER_BLOCK) {
|
|
9492
|
+
const pm = block;
|
|
9493
|
+
for (const item of [...pm.topItems, ...pm.bottomItems]) {
|
|
9494
|
+
result.push(...item.inlines);
|
|
9495
|
+
}
|
|
9496
|
+
}
|
|
9497
|
+
}
|
|
9498
|
+
return result;
|
|
9499
|
+
}
|
|
9500
|
+
function transformNumberLine(numberLine, idGenerator, inlineDragIds) {
|
|
9501
|
+
const positions = numberLine.positions.map((pos) => {
|
|
9502
|
+
const content = pos.inlines.map((inline) => transformInline(inline, idGenerator, inlineDragIds));
|
|
9503
|
+
return {
|
|
9504
|
+
id: idGenerator.generate(JSON_TYPES.NUMBER_LINE_POSITION),
|
|
9505
|
+
type: JSON_TYPES.NUMBER_LINE_POSITION,
|
|
9506
|
+
value: pos.value,
|
|
9507
|
+
content
|
|
9508
|
+
};
|
|
9509
|
+
});
|
|
9510
|
+
let counterItem;
|
|
9511
|
+
if (numberLine.counter !== void 0) {
|
|
9512
|
+
const c = numberLine.counter;
|
|
9513
|
+
counterItem = {
|
|
9514
|
+
id: idGenerator.generate(JSON_TYPES.NUMBER_LINE_COUNTER),
|
|
9515
|
+
type: JSON_TYPES.NUMBER_LINE_COUNTER,
|
|
9516
|
+
min: c.min,
|
|
9517
|
+
max: c.max,
|
|
9518
|
+
...c.default !== void 0 && { default: c.default },
|
|
9519
|
+
...c.name !== void 0 && { name: c.name },
|
|
9520
|
+
...c.answer !== void 0 && { answer: c.answer }
|
|
9521
|
+
};
|
|
9522
|
+
}
|
|
9523
|
+
let highlights;
|
|
9524
|
+
if (numberLine.highlights !== void 0 && numberLine.highlights.length > 0) {
|
|
9525
|
+
highlights = numberLine.highlights.map((h) => ({
|
|
9526
|
+
id: idGenerator.generate(JSON_TYPES.NUMBER_LINE_HIGHLIGHT),
|
|
9527
|
+
type: JSON_TYPES.NUMBER_LINE_HIGHLIGHT,
|
|
9528
|
+
from: h.from,
|
|
9529
|
+
to: h.to,
|
|
9530
|
+
...h.color !== void 0 && { color: h.color },
|
|
9531
|
+
...h.label !== void 0 && { label: h.label }
|
|
9532
|
+
}));
|
|
9533
|
+
}
|
|
9534
|
+
let cursors;
|
|
9535
|
+
if (numberLine.cursors !== void 0 && numberLine.cursors.length > 0) {
|
|
9536
|
+
cursors = numberLine.cursors.map((c) => ({
|
|
9537
|
+
id: idGenerator.generate(JSON_TYPES.NUMBER_LINE_CURSOR),
|
|
9538
|
+
type: JSON_TYPES.NUMBER_LINE_CURSOR,
|
|
9539
|
+
value: c.value,
|
|
9540
|
+
answer: c.answer,
|
|
9541
|
+
fillRange: c.fillRange,
|
|
9542
|
+
...c.color !== void 0 && { color: c.color },
|
|
9543
|
+
...c.label !== void 0 && { label: c.label }
|
|
9544
|
+
}));
|
|
9545
|
+
}
|
|
9546
|
+
let arrows;
|
|
9547
|
+
if (numberLine.arrows !== void 0 && numberLine.arrows.length > 0) {
|
|
9548
|
+
arrows = numberLine.arrows.map((a) => ({
|
|
9549
|
+
id: idGenerator.generate(JSON_TYPES.NUMBER_LINE_ARROW),
|
|
9550
|
+
type: JSON_TYPES.NUMBER_LINE_ARROW,
|
|
9551
|
+
from: a.from,
|
|
9552
|
+
to: a.to,
|
|
9553
|
+
...a.color !== void 0 && { color: a.color },
|
|
9554
|
+
content: a.inlines.map((inline) => transformInline(inline, idGenerator, inlineDragIds))
|
|
9555
|
+
}));
|
|
9556
|
+
}
|
|
9557
|
+
const item = {
|
|
9558
|
+
id: idGenerator.generate(JSON_TYPES.NUMBER_LINE),
|
|
9559
|
+
type: JSON_TYPES.NUMBER_LINE,
|
|
9560
|
+
min: numberLine.min,
|
|
9561
|
+
max: numberLine.max,
|
|
9562
|
+
step: numberLine.step,
|
|
9563
|
+
...numberLine.minorTicks !== void 0 && { minorTicks: numberLine.minorTicks },
|
|
9564
|
+
...numberLine.style !== void 0 && { style: numberLine.style },
|
|
9565
|
+
...numberLine.labelVisibility !== void 0 && { labelVisibility: numberLine.labelVisibility },
|
|
9566
|
+
...counterItem !== void 0 && { counter: counterItem },
|
|
9567
|
+
...highlights !== void 0 && { highlights },
|
|
9568
|
+
...cursors !== void 0 && { cursors },
|
|
9569
|
+
...arrows !== void 0 && { arrows },
|
|
9570
|
+
...numberLine.dragMode !== void 0 && { dragMode: numberLine.dragMode },
|
|
9571
|
+
positions
|
|
9572
|
+
};
|
|
9573
|
+
return item;
|
|
9574
|
+
}
|
|
9575
|
+
function mapMatcherItemList(items, idGenerator, inlineDragIds) {
|
|
9576
|
+
return items.map((item) => ({
|
|
9577
|
+
id: idGenerator.generate(JSON_TYPES.MATCHER_ITEM),
|
|
9578
|
+
type: JSON_TYPES.MATCHER_ITEM,
|
|
9579
|
+
itemId: item.itemId,
|
|
9580
|
+
content: item.inlines.map((inline) => transformInline(inline, idGenerator, inlineDragIds))
|
|
9581
|
+
}));
|
|
9582
|
+
}
|
|
9583
|
+
function buildMatcherWidget(matcher, idGenerator, inlineDragIds) {
|
|
9584
|
+
const rows = matcher.rows.map((row) => ({
|
|
9585
|
+
id: idGenerator.generate(JSON_TYPES.MATCHER_ROW),
|
|
9586
|
+
type: JSON_TYPES.MATCHER_ROW,
|
|
9587
|
+
items: mapMatcherItemList(row.items, idGenerator, inlineDragIds)
|
|
9588
|
+
}));
|
|
9589
|
+
const result = {
|
|
9590
|
+
type: JSON_TYPES.MATCHER,
|
|
9591
|
+
rows,
|
|
9592
|
+
groupCount: matcher.answers.length
|
|
9593
|
+
};
|
|
9594
|
+
if (matcher.colors !== void 0) {
|
|
9595
|
+
result.colors = matcher.colors;
|
|
9596
|
+
}
|
|
9597
|
+
return result;
|
|
9598
|
+
}
|
|
9599
|
+
function buildPairMatcherWidget(pm, idGenerator, inlineDragIds) {
|
|
9600
|
+
return {
|
|
9601
|
+
type: JSON_TYPES.PAIR_MATCHER,
|
|
9602
|
+
topItems: mapMatcherItemList(pm.topItems, idGenerator, inlineDragIds),
|
|
9603
|
+
bottomItems: mapMatcherItemList(pm.bottomItems, idGenerator, inlineDragIds)
|
|
9604
|
+
};
|
|
9605
|
+
}
|
|
9606
|
+
function transformVerticalOperation(vop, idGenerator) {
|
|
9607
|
+
const anyRowHasOp = vop.items.some(
|
|
9608
|
+
(item) => item.type === AST_TYPES.VOP_ROW && item.op !== void 0
|
|
9609
|
+
);
|
|
9610
|
+
if (!anyRowHasOp && vop.op === void 0) {
|
|
9611
|
+
throw new Error("VERTICAL_OPERATION: at least one op is required (set op on the block or on individual ROWs)");
|
|
9612
|
+
}
|
|
9613
|
+
let cols = 1;
|
|
9614
|
+
for (const item of vop.items) {
|
|
9615
|
+
if (item.type === AST_TYPES.VOP_ROW || item.type === AST_TYPES.VOP_CARRY_ROW) {
|
|
9616
|
+
cols = Math.max(cols, item.cells.length);
|
|
9617
|
+
}
|
|
9618
|
+
}
|
|
9619
|
+
function buildCells(row) {
|
|
9620
|
+
return row.cells.map((cell) => {
|
|
9621
|
+
const id = idGenerator.generate(JSON_TYPES.VOP_CELL);
|
|
9622
|
+
if (cell.kind === "digit") return { id, type: JSON_TYPES.VOP_CELL, kind: "digit", value: cell.value };
|
|
9623
|
+
if (cell.kind === "label") return { id, type: JSON_TYPES.VOP_CELL, kind: "label", value: cell.value };
|
|
9624
|
+
if (cell.kind === "color") return { id, type: JSON_TYPES.VOP_CELL, kind: "color", value: cell.value, color: cell.color };
|
|
9625
|
+
if (cell.kind === "bold") return { id, type: JSON_TYPES.VOP_CELL, kind: "bold", value: cell.value };
|
|
9626
|
+
if (cell.kind === "highlight") return { id, type: JSON_TYPES.VOP_CELL, kind: "highlight", value: cell.value };
|
|
9627
|
+
if (cell.kind === "input") return { id, type: JSON_TYPES.VOP_CELL, kind: "input", widgetId: cell.name };
|
|
9628
|
+
return { id, type: JSON_TYPES.VOP_CELL, kind: "empty" };
|
|
9629
|
+
});
|
|
9630
|
+
}
|
|
9631
|
+
const items = vop.items.map((item) => {
|
|
9632
|
+
if (item.type === AST_TYPES.VOP_SEPARATOR) {
|
|
9633
|
+
return {
|
|
9634
|
+
id: idGenerator.generate(JSON_TYPES.VOP_SEPARATOR),
|
|
9635
|
+
type: JSON_TYPES.VOP_SEPARATOR
|
|
9636
|
+
};
|
|
9637
|
+
}
|
|
9638
|
+
if (item.type === AST_TYPES.VOP_CARRY_ROW) {
|
|
9639
|
+
return {
|
|
9640
|
+
id: idGenerator.generate(JSON_TYPES.VOP_CARRY_ROW),
|
|
9641
|
+
type: JSON_TYPES.VOP_CARRY_ROW,
|
|
9642
|
+
cells: buildCells(item)
|
|
9643
|
+
};
|
|
9644
|
+
}
|
|
9645
|
+
const row = item;
|
|
9646
|
+
return {
|
|
9647
|
+
id: idGenerator.generate(JSON_TYPES.VOP_ROW),
|
|
9648
|
+
type: JSON_TYPES.VOP_ROW,
|
|
9649
|
+
cells: buildCells(row),
|
|
9650
|
+
...row.op !== void 0 && { op: row.op }
|
|
9651
|
+
};
|
|
9652
|
+
});
|
|
9653
|
+
return {
|
|
9654
|
+
id: idGenerator.generate(JSON_TYPES.VERTICAL_OPERATION),
|
|
9655
|
+
type: JSON_TYPES.VERTICAL_OPERATION,
|
|
9656
|
+
op: vop.op,
|
|
9657
|
+
cols,
|
|
9658
|
+
items
|
|
9659
|
+
};
|
|
9660
|
+
}
|
|
9661
|
+
function transformTable(table, idGenerator, inlineDragIds) {
|
|
9662
|
+
const rows = table.rows.map((row) => {
|
|
9663
|
+
const cells = row.cells.map((cell) => {
|
|
9664
|
+
const items = cell.inlines.map((inline) => transformInline(inline, idGenerator, inlineDragIds));
|
|
9665
|
+
const cellItem = {
|
|
9666
|
+
id: idGenerator.generate(JSON_TYPES.TABLE_CELL),
|
|
9667
|
+
type: JSON_TYPES.TABLE_CELL,
|
|
9668
|
+
items
|
|
9669
|
+
};
|
|
9670
|
+
if (cell.colspan !== void 0) {
|
|
9671
|
+
cellItem.colspan = cell.colspan;
|
|
9672
|
+
}
|
|
9673
|
+
return cellItem;
|
|
9674
|
+
});
|
|
9675
|
+
return {
|
|
9676
|
+
id: idGenerator.generate(JSON_TYPES.TABLE_ROW),
|
|
9677
|
+
type: JSON_TYPES.TABLE_ROW,
|
|
9678
|
+
cells
|
|
9679
|
+
};
|
|
9680
|
+
});
|
|
9681
|
+
return {
|
|
9682
|
+
id: idGenerator.generate(JSON_TYPES.TABLE),
|
|
9683
|
+
type: JSON_TYPES.TABLE,
|
|
9684
|
+
cols: table.cols,
|
|
9685
|
+
rows
|
|
9686
|
+
};
|
|
9687
|
+
}
|
|
9688
|
+
function transformLine(line, idGenerator, inlineDragIds) {
|
|
9689
|
+
const items = [];
|
|
9690
|
+
for (const inline of line.inlines) {
|
|
9691
|
+
items.push(transformInline(inline, idGenerator, inlineDragIds));
|
|
9692
|
+
}
|
|
9693
|
+
items.push({
|
|
9694
|
+
id: idGenerator.generate(JSON_TYPES.LINEBREAK),
|
|
9695
|
+
type: JSON_TYPES.LINEBREAK
|
|
9696
|
+
});
|
|
9697
|
+
return items;
|
|
9698
|
+
}
|
|
9699
|
+
function transformInline(inline, idGenerator, inlineDragIds) {
|
|
9700
|
+
switch (inline.type) {
|
|
9701
|
+
case AST_TYPES.FREE_TEXT: {
|
|
9702
|
+
const trimmed = inline.value.trim();
|
|
9703
|
+
const operators = ["+", "-", "×", "÷", "=", "<", ">", "?"];
|
|
9704
|
+
if (operators.includes(trimmed) && inline.value.trim() === inline.value) {
|
|
9705
|
+
return {
|
|
9706
|
+
id: idGenerator.generate("operator"),
|
|
9707
|
+
type: "operator",
|
|
9708
|
+
operator: trimmed
|
|
9744
9709
|
};
|
|
9745
|
-
}
|
|
9746
|
-
|
|
9747
|
-
|
|
9710
|
+
}
|
|
9711
|
+
return {
|
|
9712
|
+
id: idGenerator.generate(JSON_TYPES.TEXT),
|
|
9713
|
+
type: JSON_TYPES.TEXT,
|
|
9714
|
+
value: inline.value
|
|
9748
9715
|
};
|
|
9749
|
-
|
|
9750
|
-
|
|
9716
|
+
}
|
|
9717
|
+
case AST_TYPES.SPACE:
|
|
9718
|
+
return {
|
|
9719
|
+
id: idGenerator.generate(JSON_TYPES.SPACE),
|
|
9720
|
+
type: JSON_TYPES.SPACE
|
|
9721
|
+
};
|
|
9722
|
+
case AST_TYPES.HIGHLIGHTED_TEXT:
|
|
9723
|
+
return {
|
|
9724
|
+
id: idGenerator.generate(JSON_TYPES.HIGHLIGHTED_TEXT),
|
|
9725
|
+
type: JSON_TYPES.HIGHLIGHTED_TEXT,
|
|
9726
|
+
value: inline.value,
|
|
9727
|
+
highlight: inline.highlight
|
|
9728
|
+
};
|
|
9729
|
+
case AST_TYPES.COLOR_TEXT:
|
|
9730
|
+
return {
|
|
9731
|
+
id: idGenerator.generate(JSON_TYPES.COLOR_TEXT),
|
|
9732
|
+
type: JSON_TYPES.COLOR_TEXT,
|
|
9733
|
+
value: inline.value,
|
|
9734
|
+
color: inline.color,
|
|
9735
|
+
highlight: inline.highlight
|
|
9736
|
+
};
|
|
9737
|
+
case AST_TYPES.BOLD_TEXT:
|
|
9738
|
+
return {
|
|
9739
|
+
id: idGenerator.generate(JSON_TYPES.BOLD_TEXT),
|
|
9740
|
+
type: JSON_TYPES.BOLD_TEXT,
|
|
9741
|
+
value: inline.value,
|
|
9742
|
+
highlight: inline.highlight
|
|
9751
9743
|
};
|
|
9752
|
-
|
|
9753
|
-
|
|
9744
|
+
case AST_TYPES.INLINE_INPUT:
|
|
9745
|
+
return {
|
|
9746
|
+
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
9747
|
+
type: JSON_TYPES.WIDGET_REF,
|
|
9748
|
+
widgetId: inline.name
|
|
9754
9749
|
};
|
|
9755
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
return baseInvoke(value, path, args);
|
|
9761
|
-
});
|
|
9762
|
-
});
|
|
9763
|
-
LazyWrapper.prototype.reject = function(predicate) {
|
|
9764
|
-
return this.filter(negate(getIteratee(predicate)));
|
|
9750
|
+
case AST_TYPES.FRACTION:
|
|
9751
|
+
return {
|
|
9752
|
+
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
9753
|
+
type: JSON_TYPES.WIDGET_REF,
|
|
9754
|
+
widgetId: inline.name
|
|
9765
9755
|
};
|
|
9766
|
-
|
|
9767
|
-
|
|
9768
|
-
|
|
9769
|
-
|
|
9770
|
-
|
|
9771
|
-
|
|
9772
|
-
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
result2 = result2.drop(start);
|
|
9776
|
-
}
|
|
9777
|
-
if (end !== undefined$1) {
|
|
9778
|
-
end = toInteger(end);
|
|
9779
|
-
result2 = end < 0 ? result2.dropRight(-end) : result2.take(end - start);
|
|
9780
|
-
}
|
|
9781
|
-
return result2;
|
|
9756
|
+
case AST_TYPES.IMAGE:
|
|
9757
|
+
return {
|
|
9758
|
+
id: idGenerator.generate(JSON_TYPES.IMAGE),
|
|
9759
|
+
type: JSON_TYPES.IMAGE,
|
|
9760
|
+
name: inline.name,
|
|
9761
|
+
src: inline.src,
|
|
9762
|
+
alt: inline.alt,
|
|
9763
|
+
...inline.width !== void 0 && { width: inline.width },
|
|
9764
|
+
...inline.height !== void 0 && { height: inline.height }
|
|
9782
9765
|
};
|
|
9783
|
-
|
|
9784
|
-
|
|
9766
|
+
case AST_TYPES.VISUAL_FRACTION:
|
|
9767
|
+
return {
|
|
9768
|
+
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
9769
|
+
type: JSON_TYPES.WIDGET_REF,
|
|
9770
|
+
widgetId: inline.name
|
|
9785
9771
|
};
|
|
9786
|
-
|
|
9787
|
-
|
|
9772
|
+
case AST_TYPES.INLINE_DRAG_OPTION:
|
|
9773
|
+
return {
|
|
9774
|
+
id: idGenerator.generate(JSON_TYPES.WIDGET_REF),
|
|
9775
|
+
type: JSON_TYPES.WIDGET_REF,
|
|
9776
|
+
widgetId: inlineDragIds.get(inline)
|
|
9788
9777
|
};
|
|
9789
|
-
|
|
9790
|
-
|
|
9791
|
-
|
|
9792
|
-
|
|
9793
|
-
|
|
9794
|
-
|
|
9795
|
-
|
|
9796
|
-
|
|
9797
|
-
|
|
9798
|
-
|
|
9799
|
-
|
|
9800
|
-
|
|
9801
|
-
isLazy = useLazy = false;
|
|
9802
|
-
}
|
|
9803
|
-
var chainAll = this.__chain__, isHybrid = !!this.__actions__.length, isUnwrapped = retUnwrapped && !chainAll, onlyLazy = isLazy && !isHybrid;
|
|
9804
|
-
if (!retUnwrapped && useLazy) {
|
|
9805
|
-
value = onlyLazy ? value : new LazyWrapper(this);
|
|
9806
|
-
var result2 = func.apply(value, args);
|
|
9807
|
-
result2.__actions__.push({ "func": thru, "args": [interceptor], "thisArg": undefined$1 });
|
|
9808
|
-
return new LodashWrapper(result2, chainAll);
|
|
9809
|
-
}
|
|
9810
|
-
if (isUnwrapped && onlyLazy) {
|
|
9811
|
-
return func.apply(this, args);
|
|
9812
|
-
}
|
|
9813
|
-
result2 = this.thru(interceptor);
|
|
9814
|
-
return isUnwrapped ? isTaker ? result2.value()[0] : result2.value() : result2;
|
|
9815
|
-
};
|
|
9816
|
-
});
|
|
9817
|
-
arrayEach(["pop", "push", "shift", "sort", "splice", "unshift"], function(methodName) {
|
|
9818
|
-
var func = arrayProto[methodName], chainName = /^(?:push|sort|unshift)$/.test(methodName) ? "tap" : "thru", retUnwrapped = /^(?:pop|shift)$/.test(methodName);
|
|
9819
|
-
lodash2.prototype[methodName] = function() {
|
|
9820
|
-
var args = arguments;
|
|
9821
|
-
if (retUnwrapped && !this.__chain__) {
|
|
9822
|
-
var value = this.value();
|
|
9823
|
-
return func.apply(isArray(value) ? value : [], args);
|
|
9824
|
-
}
|
|
9825
|
-
return this[chainName](function(value2) {
|
|
9826
|
-
return func.apply(isArray(value2) ? value2 : [], args);
|
|
9827
|
-
});
|
|
9828
|
-
};
|
|
9829
|
-
});
|
|
9830
|
-
baseForOwn(LazyWrapper.prototype, function(func, methodName) {
|
|
9831
|
-
var lodashFunc = lodash2[methodName];
|
|
9832
|
-
if (lodashFunc) {
|
|
9833
|
-
var key2 = lodashFunc.name + "";
|
|
9834
|
-
if (!hasOwnProperty.call(realNames, key2)) {
|
|
9835
|
-
realNames[key2] = [];
|
|
9836
|
-
}
|
|
9837
|
-
realNames[key2].push({ "name": methodName, "func": lodashFunc });
|
|
9838
|
-
}
|
|
9839
|
-
});
|
|
9840
|
-
realNames[createHybrid(undefined$1, WRAP_BIND_KEY_FLAG).name] = [{
|
|
9841
|
-
"name": "wrapper",
|
|
9842
|
-
"func": undefined$1
|
|
9843
|
-
}];
|
|
9844
|
-
LazyWrapper.prototype.clone = lazyClone;
|
|
9845
|
-
LazyWrapper.prototype.reverse = lazyReverse;
|
|
9846
|
-
LazyWrapper.prototype.value = lazyValue;
|
|
9847
|
-
lodash2.prototype.at = wrapperAt;
|
|
9848
|
-
lodash2.prototype.chain = wrapperChain;
|
|
9849
|
-
lodash2.prototype.commit = wrapperCommit;
|
|
9850
|
-
lodash2.prototype.next = wrapperNext;
|
|
9851
|
-
lodash2.prototype.plant = wrapperPlant;
|
|
9852
|
-
lodash2.prototype.reverse = wrapperReverse;
|
|
9853
|
-
lodash2.prototype.toJSON = lodash2.prototype.valueOf = lodash2.prototype.value = wrapperValue;
|
|
9854
|
-
lodash2.prototype.first = lodash2.prototype.head;
|
|
9855
|
-
if (symIterator) {
|
|
9856
|
-
lodash2.prototype[symIterator] = wrapperToIterator;
|
|
9778
|
+
case AST_TYPES.CLOCK: {
|
|
9779
|
+
const base = {
|
|
9780
|
+
id: idGenerator.generate(JSON_TYPES.CLOCK),
|
|
9781
|
+
type: JSON_TYPES.CLOCK,
|
|
9782
|
+
mode: inline.mode,
|
|
9783
|
+
clockType: inline.clockType,
|
|
9784
|
+
value: inline.value,
|
|
9785
|
+
...inline.format !== void 0 && { format: inline.format },
|
|
9786
|
+
...inline.name !== void 0 && { name: inline.name }
|
|
9787
|
+
};
|
|
9788
|
+
if (inline.mode === "interactive") {
|
|
9789
|
+
return { ...base, ...inline.snap !== void 0 && { snap: inline.snap } };
|
|
9857
9790
|
}
|
|
9858
|
-
return
|
|
9791
|
+
return base;
|
|
9792
|
+
}
|
|
9793
|
+
default:
|
|
9794
|
+
throw new Error(`Unknown inline type: ${inline.type}`);
|
|
9795
|
+
}
|
|
9796
|
+
}
|
|
9797
|
+
function transformValidation(validation, answers, widgets) {
|
|
9798
|
+
if (validation.type === AST_TYPES.SIMPLE_VALIDATION) {
|
|
9799
|
+
return {
|
|
9800
|
+
mode: VALIDATION_MODES.SIMPLE,
|
|
9801
|
+
answers
|
|
9859
9802
|
};
|
|
9860
|
-
|
|
9861
|
-
|
|
9862
|
-
|
|
9863
|
-
|
|
9864
|
-
|
|
9865
|
-
|
|
9803
|
+
} else if (validation.type === AST_TYPES.EXPRESSION_VALIDATION) {
|
|
9804
|
+
const validVariableNames = Object.keys(widgets);
|
|
9805
|
+
const variables = extractVariables$1(validation.expression, validVariableNames);
|
|
9806
|
+
return {
|
|
9807
|
+
mode: VALIDATION_MODES.EXPRESSION,
|
|
9808
|
+
expression: validation.expression,
|
|
9809
|
+
variables
|
|
9810
|
+
};
|
|
9811
|
+
} else if (validation.type === AST_TYPES.MIXED_VALIDATION) {
|
|
9812
|
+
const validVariableNames = Object.keys(widgets);
|
|
9813
|
+
const variables = extractVariables$1(validation.expression, validVariableNames);
|
|
9814
|
+
const inExpression = new Set(variables);
|
|
9815
|
+
for (const [name, widget] of Object.entries(widgets)) {
|
|
9816
|
+
if (name.startsWith("_")) continue;
|
|
9817
|
+
if (name in answers) continue;
|
|
9818
|
+
if (inExpression.has(name)) continue;
|
|
9819
|
+
if (widget.type === JSON_TYPES.FRACTION) {
|
|
9820
|
+
const w = widget;
|
|
9821
|
+
if (w.numerator !== null && w.denominator !== null) continue;
|
|
9822
|
+
}
|
|
9823
|
+
if (widget.type === JSON_TYPES.VISUAL_FRACTION) {
|
|
9824
|
+
const w = widget;
|
|
9825
|
+
if (Array.isArray(w.filled) && w.filled.length > 0) continue;
|
|
9826
|
+
}
|
|
9827
|
+
throw new Error(
|
|
9828
|
+
`Mixed validation: widget "${name}" has no \`answer\` and is not referenced in expression "${validation.expression}"`
|
|
9829
|
+
);
|
|
9866
9830
|
}
|
|
9867
|
-
|
|
9868
|
-
|
|
9869
|
-
|
|
9831
|
+
return {
|
|
9832
|
+
mode: VALIDATION_MODES.MIXED,
|
|
9833
|
+
expression: validation.expression,
|
|
9834
|
+
answers,
|
|
9835
|
+
variables
|
|
9836
|
+
};
|
|
9837
|
+
}
|
|
9838
|
+
throw new Error(`Unknown validation type: ${validation.type}`);
|
|
9839
|
+
}
|
|
9840
|
+
function extractVariables$1(expression, validVariables) {
|
|
9841
|
+
const matches = expression.match(/\b[a-zA-Z_][a-zA-Z0-9_]*\b/g) || [];
|
|
9842
|
+
const variables = matches.filter(
|
|
9843
|
+
(v) => v !== "true" && v !== "false" && v !== "null" && v !== "undefined"
|
|
9844
|
+
);
|
|
9845
|
+
const uniqueVars = Array.from(new Set(variables));
|
|
9846
|
+
if (validVariables && validVariables.length > 0) {
|
|
9847
|
+
return uniqueVars.filter((v) => validVariables.includes(v));
|
|
9848
|
+
}
|
|
9849
|
+
return uniqueVars;
|
|
9850
|
+
}
|
|
9851
|
+
function normalize(value) {
|
|
9852
|
+
if (typeof value === "number") {
|
|
9853
|
+
return String(Math.round(value * 10 ** INTERNAL_PRECISION) / 10 ** INTERNAL_PRECISION);
|
|
9854
|
+
}
|
|
9855
|
+
const trimmed = String(value).trim().toLowerCase().replace(/^["']|["']$/g, "");
|
|
9856
|
+
const asNum = Number(trimmed);
|
|
9857
|
+
if (trimmed !== "" && !isNaN(asNum)) {
|
|
9858
|
+
return String(Math.round(asNum * 10 ** INTERNAL_PRECISION) / 10 ** INTERNAL_PRECISION);
|
|
9859
|
+
}
|
|
9860
|
+
return trimmed;
|
|
9861
|
+
}
|
|
9862
|
+
const NL_DRAG_PREFIX = "\0nl-drag\0";
|
|
9863
|
+
const isNLDragEncoded = (v) => v.startsWith(NL_DRAG_PREFIX);
|
|
9864
|
+
const encodeNLDragValue = (label, position) => `${NL_DRAG_PREFIX}${label}@${String(position)}`;
|
|
9865
|
+
function isNLDragCorrect(encoded, expectedLabel, expectedPosition) {
|
|
9866
|
+
const payload = encoded.slice(NL_DRAG_PREFIX.length);
|
|
9867
|
+
const atIdx = payload.lastIndexOf("@");
|
|
9868
|
+
const label = normalize(payload.slice(0, atIdx));
|
|
9869
|
+
const position = normalize(payload.slice(atIdx + 1));
|
|
9870
|
+
return label === normalize(expectedLabel) && position === normalize(expectedPosition);
|
|
9871
|
+
}
|
|
9870
9872
|
function areMatcherEqual(expected, result) {
|
|
9871
9873
|
if (!Array.isArray(result)) return false;
|
|
9872
9874
|
if (expected.length !== result.length) return false;
|
|
@@ -15245,21 +15247,19 @@ const DragOptions = ({
|
|
|
15245
15247
|
options,
|
|
15246
15248
|
onOptionClick
|
|
15247
15249
|
}) => {
|
|
15248
|
-
|
|
15249
|
-
|
|
15250
|
-
|
|
15251
|
-
|
|
15252
|
-
|
|
15253
|
-
|
|
15254
|
-
|
|
15255
|
-
|
|
15256
|
-
|
|
15257
|
-
|
|
15258
|
-
|
|
15259
|
-
|
|
15260
|
-
|
|
15261
|
-
optionId
|
|
15262
|
-
)) }) });
|
|
15250
|
+
return /* @__PURE__ */ jsxRuntime.jsx(PillBackground, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "drag-options", children: options.map((option, idx) => {
|
|
15251
|
+
const optionId = `drag-option-${idx}`;
|
|
15252
|
+
const optionValue = String(option);
|
|
15253
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
15254
|
+
SingleDragOption,
|
|
15255
|
+
{
|
|
15256
|
+
optionId,
|
|
15257
|
+
value: optionValue,
|
|
15258
|
+
onOptionClick
|
|
15259
|
+
},
|
|
15260
|
+
optionId
|
|
15261
|
+
);
|
|
15262
|
+
}) }) });
|
|
15263
15263
|
};
|
|
15264
15264
|
const InlineDragOption = ({
|
|
15265
15265
|
widgetId,
|
|
@@ -15843,8 +15843,7 @@ const RadioOptions = ({
|
|
|
15843
15843
|
onChange
|
|
15844
15844
|
}) => {
|
|
15845
15845
|
const playSound = useSound();
|
|
15846
|
-
|
|
15847
|
-
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "radio-options", children: shuffledOptions.map((option, idx) => {
|
|
15846
|
+
return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "radio-options", children: options.map((option, idx) => {
|
|
15848
15847
|
const isSelected = value === option;
|
|
15849
15848
|
const statusClass = isSelected && validationStatus ? ` widget--${validationStatus}` : "";
|
|
15850
15849
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -17677,26 +17676,42 @@ const NumberLineRenderer = ({
|
|
|
17677
17676
|
positions,
|
|
17678
17677
|
cursors
|
|
17679
17678
|
} = item;
|
|
17680
|
-
const [counterCount, setCounterCount] = React.useState(
|
|
17681
|
-
counter
|
|
17682
|
-
|
|
17679
|
+
const [counterCount, setCounterCount] = React.useState(() => {
|
|
17680
|
+
if (counter === void 0) return 0;
|
|
17681
|
+
const raw = values[counter.id];
|
|
17682
|
+
const n = typeof raw === "string" && raw !== "" ? Number(raw) : NaN;
|
|
17683
|
+
return Number.isFinite(n) ? Math.min(counter.max, Math.max(counter.min, n)) : counter.default ?? counter.min;
|
|
17684
|
+
});
|
|
17683
17685
|
const defaultCounterCount = counter !== void 0 ? counter.default ?? counter.min : 0;
|
|
17686
|
+
const prevCounterKey = React.useRef(null);
|
|
17684
17687
|
React.useEffect(() => {
|
|
17685
|
-
|
|
17686
|
-
|
|
17688
|
+
const key2 = counter ? `${counter.id}:${defaultCounterCount}` : "";
|
|
17689
|
+
const changed = prevCounterKey.current !== null && prevCounterKey.current !== key2;
|
|
17690
|
+
prevCounterKey.current = key2;
|
|
17691
|
+
if (changed) setCounterCount(defaultCounterCount);
|
|
17692
|
+
}, [counter, defaultCounterCount]);
|
|
17687
17693
|
const onChangeRef = React.useRef(onChange);
|
|
17688
17694
|
onChangeRef.current = onChange;
|
|
17695
|
+
const valuesRef = React.useRef(values);
|
|
17696
|
+
valuesRef.current = values;
|
|
17689
17697
|
React.useEffect(() => {
|
|
17690
|
-
if (counter
|
|
17691
|
-
|
|
17698
|
+
if (counter === void 0) return;
|
|
17699
|
+
const next = String(counterCount);
|
|
17700
|
+
if (valuesRef.current[counter.id] !== next) {
|
|
17701
|
+
onChangeRef.current(counter.id, next);
|
|
17692
17702
|
}
|
|
17693
17703
|
}, [counterCount, counter?.id]);
|
|
17694
17704
|
const cursorKeys = React.useMemo(
|
|
17695
17705
|
() => (cursors ?? []).map((c) => `${c.id}:${c.value}`).join(","),
|
|
17696
17706
|
[cursors]
|
|
17697
17707
|
);
|
|
17708
|
+
const prevCursorKeys = React.useRef(null);
|
|
17698
17709
|
React.useEffect(() => {
|
|
17710
|
+
const isDefinitionChange = prevCursorKeys.current !== null && prevCursorKeys.current !== cursorKeys;
|
|
17711
|
+
prevCursorKeys.current = cursorKeys;
|
|
17699
17712
|
for (const cursor of cursors ?? []) {
|
|
17713
|
+
const existing = valuesRef.current[cursor.id];
|
|
17714
|
+
if (!isDefinitionChange && existing !== void 0 && existing !== "") continue;
|
|
17700
17715
|
onChangeRef.current(cursor.id, String(cursor.value));
|
|
17701
17716
|
}
|
|
17702
17717
|
}, [cursorKeys]);
|
|
@@ -18136,6 +18151,8 @@ function groupLineItems(items) {
|
|
|
18136
18151
|
return groups;
|
|
18137
18152
|
}
|
|
18138
18153
|
const toValidationStatus = (v) => v === true ? "correct" : v === false ? "incorrect" : null;
|
|
18154
|
+
const isNumericText = (value) => /^[\d.,\s]+$/.test(value);
|
|
18155
|
+
const numberClass = (value) => isNumericText(value) ? "text-item-number" : "";
|
|
18139
18156
|
const LayoutRenderer = ({
|
|
18140
18157
|
item,
|
|
18141
18158
|
widgets,
|
|
@@ -18152,47 +18169,49 @@ const LayoutRenderer = ({
|
|
|
18152
18169
|
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: `question-text ${hasArabic ? "question-text-arabic" : ""}`, children: item.value });
|
|
18153
18170
|
}
|
|
18154
18171
|
case JSON_TYPES.TEXT: {
|
|
18155
|
-
|
|
18156
|
-
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-item ${isNum ? "text-item-number" : ""} ${isArabic(item.value) ? "text-item-arabic" : "text-item-not-arabic"}`, children: item.value });
|
|
18172
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: `text-item ${numberClass(item.value)} ${isArabic(item.value) ? "text-item-arabic" : "text-item-not-arabic"}`, children: item.value });
|
|
18157
18173
|
}
|
|
18158
18174
|
case JSON_TYPES.HIGHLIGHTED_TEXT: {
|
|
18159
18175
|
const hasArabic = isArabic(item.value);
|
|
18160
18176
|
const dirAttr = !hasArabic ? "ltr" : void 0;
|
|
18177
|
+
const numClass = numberClass(item.value);
|
|
18161
18178
|
if (item.highlight) {
|
|
18162
18179
|
const [start, end] = item.highlight;
|
|
18163
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className:
|
|
18180
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `highlighted-text text-item ${numClass}`, dir: dirAttr, children: [
|
|
18164
18181
|
item.value.slice(0, start),
|
|
18165
18182
|
/* @__PURE__ */ jsxRuntime.jsx("mark", { className: "highlight text-item", children: item.value.slice(start, end) }),
|
|
18166
18183
|
item.value.slice(end)
|
|
18167
18184
|
] });
|
|
18168
18185
|
}
|
|
18169
|
-
return /* @__PURE__ */ jsxRuntime.jsx("mark", { className:
|
|
18186
|
+
return /* @__PURE__ */ jsxRuntime.jsx("mark", { className: `highlighted-text text-item ${numClass}`, dir: dirAttr, children: item.value });
|
|
18170
18187
|
}
|
|
18171
18188
|
case JSON_TYPES.COLOR_TEXT: {
|
|
18172
18189
|
const hasArabic = isArabic(item.value);
|
|
18173
18190
|
const dirAttr = !hasArabic ? "ltr" : void 0;
|
|
18191
|
+
const numClass = numberClass(item.value);
|
|
18174
18192
|
if (item.highlight) {
|
|
18175
18193
|
const [start, end] = item.highlight;
|
|
18176
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className:
|
|
18194
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `color-text text-item ${numClass}`, dir: dirAttr, children: [
|
|
18177
18195
|
item.value.slice(0, start),
|
|
18178
18196
|
/* @__PURE__ */ jsxRuntime.jsx("span", { className: "color-text-colored", style: { color: item.color }, children: item.value.slice(start, end) }),
|
|
18179
18197
|
item.value.slice(end)
|
|
18180
18198
|
] });
|
|
18181
18199
|
}
|
|
18182
|
-
return /* @__PURE__ */ jsxRuntime.jsx("span", { className:
|
|
18200
|
+
return /* @__PURE__ */ jsxRuntime.jsx("span", { className: `color-text text-item ${numClass}`, style: { color: item.color }, dir: dirAttr, children: item.value });
|
|
18183
18201
|
}
|
|
18184
18202
|
case JSON_TYPES.BOLD_TEXT: {
|
|
18185
18203
|
const hasArabic = isArabic(item.value);
|
|
18186
18204
|
const dirAttr = !hasArabic ? "ltr" : void 0;
|
|
18205
|
+
const numClass = numberClass(item.value);
|
|
18187
18206
|
if (item.highlight) {
|
|
18188
18207
|
const [start, end] = item.highlight;
|
|
18189
|
-
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className:
|
|
18208
|
+
return /* @__PURE__ */ jsxRuntime.jsxs("span", { className: `bold-text text-item ${numClass}`, dir: dirAttr, children: [
|
|
18190
18209
|
item.value.slice(0, start),
|
|
18191
18210
|
/* @__PURE__ */ jsxRuntime.jsx("strong", { className: "bold-text-strong text-item", children: item.value.slice(start, end) }),
|
|
18192
18211
|
item.value.slice(end)
|
|
18193
18212
|
] });
|
|
18194
18213
|
}
|
|
18195
|
-
return /* @__PURE__ */ jsxRuntime.jsx("strong", { className:
|
|
18214
|
+
return /* @__PURE__ */ jsxRuntime.jsx("strong", { className: `bold-text text-item ${numClass}`, dir: dirAttr, children: item.value });
|
|
18196
18215
|
}
|
|
18197
18216
|
case JSON_TYPES.IMAGE:
|
|
18198
18217
|
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
@@ -18444,6 +18463,44 @@ function buildInitialValues(json) {
|
|
|
18444
18463
|
}
|
|
18445
18464
|
return initial;
|
|
18446
18465
|
}
|
|
18466
|
+
function buildRevealedValues(json) {
|
|
18467
|
+
const revealed = {};
|
|
18468
|
+
const validation = json.validation;
|
|
18469
|
+
if (!validation) return revealed;
|
|
18470
|
+
const answers = validation.answers ?? {};
|
|
18471
|
+
for (const [widgetId, expected] of Object.entries(answers)) {
|
|
18472
|
+
const widget = json.widgets[widgetId];
|
|
18473
|
+
if (widget?.type === JSON_TYPES.FRACTION && typeof expected === "string") {
|
|
18474
|
+
const [num, den] = expected.split("/", 2);
|
|
18475
|
+
if (widget.numerator === null && num !== void 0) revealed[getFractionKey(widgetId, "num")] = num;
|
|
18476
|
+
if (widget.denominator === null && den !== void 0) revealed[getFractionKey(widgetId, "den")] = den;
|
|
18477
|
+
continue;
|
|
18478
|
+
}
|
|
18479
|
+
revealed[widgetId] = typeof expected === "number" ? String(expected) : expected;
|
|
18480
|
+
}
|
|
18481
|
+
if ((validation.mode === VALIDATION_MODES.EXPRESSION || validation.mode === VALIDATION_MODES.MIXED) && validation.expression) {
|
|
18482
|
+
const vars = validation.variables ?? [];
|
|
18483
|
+
const freeVars = vars.filter((v) => !(v in answers));
|
|
18484
|
+
if (freeVars.length > 0) {
|
|
18485
|
+
const ranges = {};
|
|
18486
|
+
for (const v of vars) {
|
|
18487
|
+
if (v in answers) {
|
|
18488
|
+
const pinned = Number(answers[v]);
|
|
18489
|
+
if (Number.isFinite(pinned)) ranges[v] = { min: pinned, max: pinned, step: 1 };
|
|
18490
|
+
} else {
|
|
18491
|
+
ranges[v] = { min: 1, max: 100, step: 1 };
|
|
18492
|
+
}
|
|
18493
|
+
}
|
|
18494
|
+
const combo = findValidCombination(validation.expression, ranges);
|
|
18495
|
+
if (combo) {
|
|
18496
|
+
for (const v of freeVars) {
|
|
18497
|
+
if (combo[v] !== void 0) revealed[v] = String(combo[v]);
|
|
18498
|
+
}
|
|
18499
|
+
}
|
|
18500
|
+
}
|
|
18501
|
+
}
|
|
18502
|
+
return revealed;
|
|
18503
|
+
}
|
|
18447
18504
|
const ExerciseRenderer = React.forwardRef(({
|
|
18448
18505
|
json,
|
|
18449
18506
|
onSubmit,
|
|
@@ -18454,10 +18511,13 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
18454
18511
|
sounds
|
|
18455
18512
|
}, ref) => {
|
|
18456
18513
|
const [values, setValues] = React.useState(() => buildInitialValues(json));
|
|
18514
|
+
const valuesRef = React.useRef(values);
|
|
18515
|
+
valuesRef.current = values;
|
|
18457
18516
|
const [droppedSources, setDroppedSources] = React.useState(/* @__PURE__ */ new Map());
|
|
18458
18517
|
const [focusedInputId, setFocusedInputId] = React.useState(null);
|
|
18459
18518
|
const [validationDetails, setValidationDetails] = React.useState(null);
|
|
18460
18519
|
const [resetKey, setResetKey] = React.useState(0);
|
|
18520
|
+
const [revealed, setRevealed] = React.useState(false);
|
|
18461
18521
|
const playSound = useSoundPlayer(soundEnabled, sounds);
|
|
18462
18522
|
React.useImperativeHandle(ref, () => ({
|
|
18463
18523
|
submit: () => {
|
|
@@ -18491,7 +18551,7 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
18491
18551
|
getValues: () => values,
|
|
18492
18552
|
getCompletion: () => {
|
|
18493
18553
|
const answerable = getAnswerableInputs(json);
|
|
18494
|
-
const missing = getMissingInputs(json,
|
|
18554
|
+
const missing = getMissingInputs(json, valuesRef.current);
|
|
18495
18555
|
const missingSet = new Set(missing);
|
|
18496
18556
|
const filledIds = answerable.filter((id) => !missingSet.has(id));
|
|
18497
18557
|
return {
|
|
@@ -18504,11 +18564,28 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
18504
18564
|
};
|
|
18505
18565
|
},
|
|
18506
18566
|
reset: () => {
|
|
18507
|
-
|
|
18567
|
+
const initial = buildInitialValues(json);
|
|
18568
|
+
valuesRef.current = initial;
|
|
18569
|
+
setValues(initial);
|
|
18508
18570
|
setDroppedSources(/* @__PURE__ */ new Map());
|
|
18509
18571
|
setFocusedInputId(null);
|
|
18510
18572
|
setValidationDetails(null);
|
|
18511
18573
|
setResetKey((k) => k + 1);
|
|
18574
|
+
setRevealed(false);
|
|
18575
|
+
},
|
|
18576
|
+
revealAnswers: () => {
|
|
18577
|
+
const revealedValues = buildRevealedValues(json);
|
|
18578
|
+
const details = {};
|
|
18579
|
+
for (const [widgetId, userValue] of Object.entries(revealedValues)) {
|
|
18580
|
+
details[widgetId] = { widgetId, userValue, isCorrect: true };
|
|
18581
|
+
}
|
|
18582
|
+
const next = { ...valuesRef.current, ...revealedValues };
|
|
18583
|
+
valuesRef.current = next;
|
|
18584
|
+
setValues(next);
|
|
18585
|
+
setDroppedSources(/* @__PURE__ */ new Map());
|
|
18586
|
+
setValidationDetails(details);
|
|
18587
|
+
setRevealed(true);
|
|
18588
|
+
setResetKey((k) => k + 1);
|
|
18512
18589
|
},
|
|
18513
18590
|
focusFirstInput: () => {
|
|
18514
18591
|
const container = document.querySelector(".math-exercise-renderer");
|
|
@@ -18519,17 +18596,20 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
18519
18596
|
}
|
|
18520
18597
|
}), [json, values, onSubmit, playSound]);
|
|
18521
18598
|
const handleChange = (widgetId, value, fromDrop = false, sourceOptionId) => {
|
|
18522
|
-
|
|
18599
|
+
setRevealed(false);
|
|
18600
|
+
const next = { ...valuesRef.current, [widgetId]: value };
|
|
18601
|
+
valuesRef.current = next;
|
|
18602
|
+
setValues(next);
|
|
18523
18603
|
setDroppedSources((prev) => {
|
|
18524
|
-
const
|
|
18604
|
+
const next2 = new Map(prev);
|
|
18525
18605
|
if (fromDrop && typeof value === "string" && value !== "" && sourceOptionId) {
|
|
18526
|
-
|
|
18606
|
+
next2.set(widgetId, sourceOptionId);
|
|
18527
18607
|
} else {
|
|
18528
|
-
|
|
18608
|
+
next2.delete(widgetId);
|
|
18529
18609
|
}
|
|
18530
|
-
return
|
|
18610
|
+
return next2;
|
|
18531
18611
|
});
|
|
18532
|
-
onChange?.(
|
|
18612
|
+
onChange?.(next);
|
|
18533
18613
|
};
|
|
18534
18614
|
const handleSubmit = () => {
|
|
18535
18615
|
onSubmit?.(values);
|
|
@@ -18647,7 +18727,7 @@ const ExerciseRenderer = React.forwardRef(({
|
|
|
18647
18727
|
if (setSquareWidget) add2("set-square-1", "set-square", Math.round(getSize(setSquareWidget) * 160));
|
|
18648
18728
|
return { tools: list, toolsMinHeight: maxH };
|
|
18649
18729
|
}, [rulerWidget, protractorWidget, setSquareWidget]);
|
|
18650
|
-
return /* @__PURE__ */ jsxRuntime.jsx(SoundPlayerProvider, { play: playSound, children: /* @__PURE__ */ jsxRuntime.jsx(ExerciseDndProvider, { droppedSources, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `math-exercise-renderer ${className}`, children: [
|
|
18730
|
+
return /* @__PURE__ */ jsxRuntime.jsx(SoundPlayerProvider, { play: playSound, children: /* @__PURE__ */ jsxRuntime.jsx(ExerciseDndProvider, { droppedSources, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `math-exercise-renderer ${revealed ? "revealed" : ""} ${className}`, children: [
|
|
18651
18731
|
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
18652
18732
|
"div",
|
|
18653
18733
|
{
|