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