@khanacademy/perseus-editor 4.1.5 → 4.1.7
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/es/index.js +1 -1
- package/dist/index.js +1 -1
- package/package.json +9 -6
- package/.babelrc.js +0 -8
- package/.eslintrc.js +0 -12
- package/CHANGELOG.md +0 -1155
- package/src/README.md +0 -7
- package/src/__stories__/editor-page.stories.tsx +0 -65
- package/src/__stories__/editor.stories.tsx +0 -134
- package/src/__stories__/item-editor.stories.tsx +0 -41
- package/src/__stories__/tex-error-view.stories.tsx +0 -26
- package/src/__testdata__/input-number.testdata.ts +0 -27
- package/src/__tests__/editor.test.tsx +0 -142
- package/src/__tests__/hint-editor.test.tsx +0 -83
- package/src/__tests__/i18n.test.ts +0 -501
- package/src/__tests__/integration.test.ts +0 -5
- package/src/__tests__/item-extras-editor.test.tsx +0 -85
- package/src/__tests__/traversal.test.ts +0 -377
- package/src/all-editors.test.ts +0 -16
- package/src/all-editors.ts +0 -83
- package/src/all-widgets.ts +0 -7
- package/src/article-editor.tsx +0 -438
- package/src/components/__stories__/blur-input.stories.tsx +0 -22
- package/src/components/__stories__/section-control-button.stories.tsx +0 -26
- package/src/components/__tests__/blur-input.test.tsx +0 -67
- package/src/components/blur-input.tsx +0 -69
- package/src/components/checkbox.tsx +0 -194
- package/src/components/device-framer.tsx +0 -90
- package/src/components/drag-target.tsx +0 -87
- package/src/components/dropdown-option.tsx +0 -311
- package/src/components/form-wrapped-text-field.tsx +0 -245
- package/src/components/graph-points-count-selector.tsx +0 -31
- package/src/components/graph-settings.tsx +0 -627
- package/src/components/graph-type-selector.tsx +0 -31
- package/src/components/hover-behavior.ts +0 -177
- package/src/components/json-editor.tsx +0 -121
- package/src/components/link.ts +0 -173
- package/src/components/section-control-button.tsx +0 -53
- package/src/components/segment-count-selector.tsx +0 -27
- package/src/components/simple-button.tsx +0 -85
- package/src/components/sortable.tsx +0 -187
- package/src/components/util.ts +0 -38
- package/src/components/viewport-resizer.tsx +0 -72
- package/src/components/widget-editor.tsx +0 -222
- package/src/components/widget-select.tsx +0 -48
- package/src/diffs/__stories__/perseus-diff-wrapper.tsx +0 -11
- package/src/diffs/__stories__/structured-item-diff.stories.tsx +0 -855
- package/src/diffs/__stories__/tags-diff.stories.tsx +0 -36
- package/src/diffs/__stories__/text-diff.stories.tsx +0 -35
- package/src/diffs/__tests__/split-diff.test.ts +0 -33
- package/src/diffs/__tests__/string-array-diff.test.ts +0 -73
- package/src/diffs/__tests__/structured-item-diff.test.ts +0 -433
- package/src/diffs/__tests__/widget-diff-performer.test.ts +0 -43
- package/src/diffs/article-diff.tsx +0 -69
- package/src/diffs/item-diff.tsx +0 -76
- package/src/diffs/renderer-diff.tsx +0 -137
- package/src/diffs/split-diff.ts +0 -31
- package/src/diffs/string-array-diff.ts +0 -74
- package/src/diffs/structured-item-diff.tsx +0 -275
- package/src/diffs/tags-diff.tsx +0 -49
- package/src/diffs/text-diff.tsx +0 -193
- package/src/diffs/widget-diff-performer.ts +0 -69
- package/src/diffs/widget-diff.tsx +0 -290
- package/src/editor-page.tsx +0 -325
- package/src/editor.tsx +0 -1099
- package/src/hint-editor.tsx +0 -469
- package/src/i18n.ts +0 -140
- package/src/iframe-content-renderer.tsx +0 -188
- package/src/index.ts +0 -30
- package/src/item-editor.tsx +0 -158
- package/src/item-extras-editor.tsx +0 -230
- package/src/katex-mhchem/index.js +0 -73
- package/src/katex-mhchem/parser.js +0 -2659
- package/src/multirenderer-editor.tsx +0 -1061
- package/src/stateful-article-editor.tsx +0 -173
- package/src/stateful-editor-page.tsx +0 -88
- package/src/styles/perseus-editor.less +0 -1321
- package/src/testing-widgets.ts +0 -11
- package/src/tex-error-view.tsx +0 -90
- package/src/util/points.test.ts +0 -24
- package/src/util/points.ts +0 -13
- package/src/util/register-all-widgets-and-editors-for-testing.test.ts +0 -17
- package/src/util/register-all-widgets-and-editors-for-testing.ts +0 -17
- package/src/version.ts +0 -10
- package/src/widgets/__stories__/categorizer-editor.stories.tsx +0 -24
- package/src/widgets/__stories__/definition-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/dropdown-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/explanation-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/expression-editor.stories.tsx +0 -122
- package/src/widgets/__stories__/image-editor.stories.tsx +0 -72
- package/src/widgets/__stories__/input-number-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/interaction-editor.stories.tsx +0 -37
- package/src/widgets/__stories__/label-image-editor.stories.tsx +0 -99
- package/src/widgets/__stories__/matcher-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/number-line-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/numeric-input-editor.stories.tsx +0 -18
- package/src/widgets/__stories__/radio-editor.stories.tsx +0 -116
- package/src/widgets/__stories__/sorter-editor.stories.tsx +0 -18
- package/src/widgets/__tests__/categorizer-editor.test.tsx +0 -51
- package/src/widgets/__tests__/definition-editor.test.tsx +0 -41
- package/src/widgets/__tests__/dropdown-editor.test.tsx +0 -57
- package/src/widgets/__tests__/explanation-editor.test.tsx +0 -59
- package/src/widgets/__tests__/expression-editor.test.tsx +0 -403
- package/src/widgets/__tests__/input-number-editor.test.tsx +0 -128
- package/src/widgets/__tests__/matcher-editor.test.tsx +0 -47
- package/src/widgets/__tests__/number-line-editor.test.tsx +0 -144
- package/src/widgets/__tests__/numeric-input-editor.test.tsx +0 -177
- package/src/widgets/__tests__/radio-editor.test.tsx +0 -118
- package/src/widgets/__tests__/sorter-editor.test.tsx +0 -55
- package/src/widgets/categorizer-editor.tsx +0 -104
- package/src/widgets/cs-program-editor.tsx +0 -256
- package/src/widgets/definition-editor.tsx +0 -77
- package/src/widgets/deprecated-standin-editor.tsx +0 -29
- package/src/widgets/dropdown-editor.tsx +0 -211
- package/src/widgets/example-graphie-widget-editor.tsx +0 -70
- package/src/widgets/example-graphie-widget.tsx +0 -172
- package/src/widgets/example-widget-editor.tsx +0 -62
- package/src/widgets/example-widget.tsx +0 -154
- package/src/widgets/explanation-editor.tsx +0 -92
- package/src/widgets/expression-editor.tsx +0 -557
- package/src/widgets/graded-group-editor.tsx +0 -161
- package/src/widgets/graded-group-set-editor.tsx +0 -104
- package/src/widgets/grapher-editor.tsx +0 -155
- package/src/widgets/group-editor.tsx +0 -83
- package/src/widgets/iframe-editor.tsx +0 -202
- package/src/widgets/image-editor.tsx +0 -367
- package/src/widgets/input-number-editor.tsx +0 -288
- package/src/widgets/interaction-editor/constraint-editor.tsx +0 -134
- package/src/widgets/interaction-editor/function-editor.tsx +0 -108
- package/src/widgets/interaction-editor/index.ts +0 -3
- package/src/widgets/interaction-editor/interaction-editor.tsx +0 -749
- package/src/widgets/interaction-editor/label-editor.tsx +0 -87
- package/src/widgets/interaction-editor/line-editor.tsx +0 -125
- package/src/widgets/interaction-editor/mathquill-input.tsx +0 -81
- package/src/widgets/interaction-editor/movable-line-editor.tsx +0 -121
- package/src/widgets/interaction-editor/movable-point-editor.tsx +0 -89
- package/src/widgets/interaction-editor/parametric-editor.tsx +0 -117
- package/src/widgets/interaction-editor/point-editor.tsx +0 -72
- package/src/widgets/interaction-editor/rectangle-editor.tsx +0 -96
- package/src/widgets/interactive-graph-editor.tsx +0 -571
- package/src/widgets/label-image/__stories__/answer-choices.stories.tsx +0 -82
- package/src/widgets/label-image/__stories__/behavior.stories.tsx +0 -31
- package/src/widgets/label-image/__stories__/marker.stories.tsx +0 -75
- package/src/widgets/label-image/__stories__/question-markers.stories.tsx +0 -107
- package/src/widgets/label-image/__stories__/select-image.stories.tsx +0 -59
- package/src/widgets/label-image/answer-choices.tsx +0 -231
- package/src/widgets/label-image/behavior.tsx +0 -122
- package/src/widgets/label-image/marker.tsx +0 -290
- package/src/widgets/label-image/question-markers.tsx +0 -176
- package/src/widgets/label-image/select-image.tsx +0 -81
- package/src/widgets/label-image-editor.tsx +0 -252
- package/src/widgets/lights-puzzle-editor.tsx +0 -278
- package/src/widgets/matcher-editor.tsx +0 -149
- package/src/widgets/matrix-editor.tsx +0 -156
- package/src/widgets/measurer-editor.tsx +0 -253
- package/src/widgets/molecule-editor.tsx +0 -64
- package/src/widgets/number-line-editor.tsx +0 -512
- package/src/widgets/numeric-input-editor.tsx +0 -536
- package/src/widgets/orderer-editor.tsx +0 -172
- package/src/widgets/passage-editor.tsx +0 -117
- package/src/widgets/passage-ref-editor.tsx +0 -80
- package/src/widgets/passage-ref-target-editor.tsx +0 -49
- package/src/widgets/plotter-editor.tsx +0 -542
- package/src/widgets/radio/editor.tsx +0 -398
- package/src/widgets/reaction-diagram-editor.tsx +0 -131
- package/src/widgets/sequence-editor.tsx +0 -205
- package/src/widgets/simple-markdown-tester-editor.tsx +0 -86
- package/src/widgets/simple-markdown-tester.tsx +0 -92
- package/src/widgets/simulator-editor.tsx +0 -146
- package/src/widgets/sorter-editor.tsx +0 -103
- package/src/widgets/table-editor.tsx +0 -164
- package/src/widgets/unit-editor.tsx +0 -299
- package/src/widgets/video-editor.tsx +0 -73
- package/tsconfig-build.json +0 -22
- package/tsconfig-build.tsbuildinfo +0 -1
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Displays the given content in an iframe, isolating it from the parent page
|
|
3
|
-
*
|
|
4
|
-
* To simulate the environment of content rendered by itself, content previews
|
|
5
|
-
* are rendered inside iframes, where components such as the math keypad work
|
|
6
|
-
* because the body of the document is not the body of the editor. To make this
|
|
7
|
-
* work, this component renders an iframe and can communicate objects to it
|
|
8
|
-
* through postMessage. The recipient then needs to listen for these messages
|
|
9
|
-
* and pull out the appropriate object stored in the parent's iframeDataStore
|
|
10
|
-
* to get the data to render. When the iframe is loaded, it's javascript calls
|
|
11
|
-
* its requestIframeData function in the parent, which triggers the parent to
|
|
12
|
-
* send the current data.
|
|
13
|
-
*/
|
|
14
|
-
import {Log} from "@khanacademy/perseus";
|
|
15
|
-
import * as React from "react";
|
|
16
|
-
|
|
17
|
-
let nextIframeID = 0;
|
|
18
|
-
const requestIframeData: Record<string, any> = {};
|
|
19
|
-
const updateIframeHeight: Record<string, any> = {};
|
|
20
|
-
// @ts-expect-error - TS2339 - Property 'iframeDataStore' does not exist on type 'Window & typeof globalThis'.
|
|
21
|
-
window.iframeDataStore = {};
|
|
22
|
-
|
|
23
|
-
// This is called once after Perseus is loaded and the iframe
|
|
24
|
-
// is ready to render content, then twice a second afterwards
|
|
25
|
-
// to capture the result of animations.
|
|
26
|
-
window.addEventListener("message", (event) => {
|
|
27
|
-
if (typeof event.data === "string") {
|
|
28
|
-
// In Perseus, we expect the callback to exist, as it is added by
|
|
29
|
-
// `IframeContentRenderer.componentDidMount()`. Unfortunately, this
|
|
30
|
-
// event listener also gets added in Manticore (since we include Perseus
|
|
31
|
-
// from there), and Crowdin fires its own "message" events. So we'll
|
|
32
|
-
// just have to ignore the event when we can't find the callback.
|
|
33
|
-
const callback = requestIframeData[event.data];
|
|
34
|
-
if (callback) {
|
|
35
|
-
callback();
|
|
36
|
-
}
|
|
37
|
-
} else if (event.data.id) {
|
|
38
|
-
if (event.data.height !== undefined) {
|
|
39
|
-
updateIframeHeight[event.data.id](event.data.height);
|
|
40
|
-
} else if (event.data.lintWarnings) {
|
|
41
|
-
// This is a lint report being sent back from the linter.
|
|
42
|
-
// TODO:
|
|
43
|
-
// We'll want to display the number of warnings in the HUD.
|
|
44
|
-
// But for now, we just log it to the console
|
|
45
|
-
Log.log("LINTER REPORT", {
|
|
46
|
-
lintWarnings: JSON.stringify(event.data.lintWarnings),
|
|
47
|
-
});
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
});
|
|
51
|
-
|
|
52
|
-
type Props = {
|
|
53
|
-
// The HTML content to render to the iframe
|
|
54
|
-
// content?: string,
|
|
55
|
-
|
|
56
|
-
// The URL that the iframe should load
|
|
57
|
-
url: string;
|
|
58
|
-
// The data-* suffix for passing information to the iframe's JS
|
|
59
|
-
datasetKey: string;
|
|
60
|
-
// The value of the data-* attribute
|
|
61
|
-
datasetValue: any;
|
|
62
|
-
// Whether to make the iframe's height match its content's height,
|
|
63
|
-
// used to prevent scrolling inside the iframe.
|
|
64
|
-
seamless: boolean;
|
|
65
|
-
};
|
|
66
|
-
|
|
67
|
-
class IframeContentRenderer extends React.Component<Props> {
|
|
68
|
-
_frame: HTMLIFrameElement | null | undefined;
|
|
69
|
-
container = React.createRef<HTMLDivElement>();
|
|
70
|
-
|
|
71
|
-
// @ts-expect-error - TS2564 - Property '_isMounted' has no initializer and is not definitely assigned in the constructor.
|
|
72
|
-
_isMounted: boolean;
|
|
73
|
-
_lastData: any;
|
|
74
|
-
// @ts-expect-error - TS2564 - Property '_lastHeight' has no initializer and is not definitely assigned in the constructor.
|
|
75
|
-
_lastHeight: number;
|
|
76
|
-
// @ts-expect-error - TS2564 - Property 'iframeID' has no initializer and is not definitely assigned in the constructor.
|
|
77
|
-
iframeID: number;
|
|
78
|
-
|
|
79
|
-
componentDidMount() {
|
|
80
|
-
// TODO(scottgrant): This is a hack to remove the deprecated call to
|
|
81
|
-
// this.isMounted() but is still considered an anti-pattern.
|
|
82
|
-
this._isMounted = true;
|
|
83
|
-
|
|
84
|
-
this.iframeID = nextIframeID;
|
|
85
|
-
nextIframeID++;
|
|
86
|
-
|
|
87
|
-
this._prepareFrame();
|
|
88
|
-
requestIframeData[this.iframeID] = () => {
|
|
89
|
-
this.sendNewData(this._lastData);
|
|
90
|
-
};
|
|
91
|
-
|
|
92
|
-
updateIframeHeight[this.iframeID] = (height: any) => {
|
|
93
|
-
this._lastHeight = height;
|
|
94
|
-
if (
|
|
95
|
-
this._isMounted &&
|
|
96
|
-
this.props.seamless &&
|
|
97
|
-
this.container.current
|
|
98
|
-
) {
|
|
99
|
-
this.container.current.style.height = height + "px";
|
|
100
|
-
}
|
|
101
|
-
};
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
shouldComponentUpdate(nextProps: Props): boolean {
|
|
105
|
-
return (
|
|
106
|
-
nextProps.datasetValue !== this.props.datasetValue ||
|
|
107
|
-
nextProps.seamless !== this.props.seamless
|
|
108
|
-
);
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
componentDidUpdate(prevProps: Props) {
|
|
112
|
-
if (this.container.current) {
|
|
113
|
-
if (!this.props.seamless) {
|
|
114
|
-
this.container.current.style.height = "100%";
|
|
115
|
-
} else {
|
|
116
|
-
this.container.current.style.height = this._lastHeight + "px";
|
|
117
|
-
}
|
|
118
|
-
}
|
|
119
|
-
|
|
120
|
-
if (prevProps.datasetValue !== this.props.datasetValue) {
|
|
121
|
-
// Not just a change in seamless
|
|
122
|
-
this._prepareFrame();
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
componentWillUnmount() {
|
|
127
|
-
requestIframeData[this.iframeID] = null;
|
|
128
|
-
updateIframeHeight[this.iframeID] = null;
|
|
129
|
-
|
|
130
|
-
this._isMounted = false;
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
_prepareFrame() {
|
|
134
|
-
// Don't initialize the iframe until the page has loaded
|
|
135
|
-
if (this._frame) {
|
|
136
|
-
this.container.current?.removeChild(this._frame);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
const frame = document.createElement("iframe");
|
|
140
|
-
frame.style.width = "100%";
|
|
141
|
-
frame.style.height = "100%";
|
|
142
|
-
frame.src = this.props.url;
|
|
143
|
-
|
|
144
|
-
if (this.props.datasetKey) {
|
|
145
|
-
// If the user has specified a data-* attribute to place on the
|
|
146
|
-
// iframe, we set it here. Right now, this is used to
|
|
147
|
-
// communicate if the iframe should be enabling touch emulation.
|
|
148
|
-
frame.dataset[this.props.datasetKey] = this.props.datasetValue;
|
|
149
|
-
}
|
|
150
|
-
frame.dataset.id = String(this.iframeID);
|
|
151
|
-
|
|
152
|
-
if (this.props.seamless) {
|
|
153
|
-
// The seamless prop is the same as the "nochrome" prop that
|
|
154
|
-
// gets passed to DeviceFramer. If it is set, then we're going
|
|
155
|
-
// to be displaying editor previews and want to leave some room
|
|
156
|
-
// for lint indicators in the right margin. We use the dataset
|
|
157
|
-
// as above to pass this information on to the perseus-frame
|
|
158
|
-
// component inside the iframe
|
|
159
|
-
frame.dataset.lintGutter = "true";
|
|
160
|
-
}
|
|
161
|
-
|
|
162
|
-
this.container.current?.appendChild(frame);
|
|
163
|
-
|
|
164
|
-
this._frame = frame;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
sendNewData(data: any) {
|
|
168
|
-
const frame = this._frame;
|
|
169
|
-
if (this._isMounted && data && frame?.contentWindow) {
|
|
170
|
-
this._lastData = data;
|
|
171
|
-
|
|
172
|
-
// We can't use JSON.stringify/parse for this because the apiOptions
|
|
173
|
-
// includes the functions GroupMetadataEditor, groupAnnotator,
|
|
174
|
-
// onFocusChange, and onInputError.
|
|
175
|
-
// @ts-expect-error - TS2339 - Property 'iframeDataStore' does not exist on type 'Window & typeof globalThis'.
|
|
176
|
-
window.iframeDataStore[this.iframeID] = data;
|
|
177
|
-
frame.contentWindow.postMessage(this.iframeID, "*");
|
|
178
|
-
}
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
render(): React.ReactNode {
|
|
182
|
-
return (
|
|
183
|
-
<div ref={this.container} style={{width: "100%", height: "100%"}} />
|
|
184
|
-
);
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export default IframeContentRenderer;
|
package/src/index.ts
DELETED
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
export {libVersion} from "./version";
|
|
2
|
-
|
|
3
|
-
export {default as ArticleEditor} from "./article-editor";
|
|
4
|
-
export {default as DeviceFramer} from "./components/device-framer";
|
|
5
|
-
export {default as ViewportResizer} from "./components/viewport-resizer";
|
|
6
|
-
export {default as ArticleDiff} from "./diffs/article-diff";
|
|
7
|
-
export {default as ItemDiff} from "./diffs/item-diff";
|
|
8
|
-
export {default as StructuredItemDiff} from "./diffs/structured-item-diff";
|
|
9
|
-
export {default as EditorPage} from "./editor-page";
|
|
10
|
-
export {default as Editor} from "./editor";
|
|
11
|
-
export {default as i18n} from "./i18n";
|
|
12
|
-
export {default as IframeContentRenderer} from "./iframe-content-renderer";
|
|
13
|
-
export {default as MultiRendererEditor} from "./multirenderer-editor";
|
|
14
|
-
export {default as StatefulArticleEditor} from "./stateful-article-editor";
|
|
15
|
-
export {default as StatefulEditorPage} from "./stateful-editor-page";
|
|
16
|
-
|
|
17
|
-
import "./styles/perseus-editor.less";
|
|
18
|
-
|
|
19
|
-
// eslint-disable-next-line import/order
|
|
20
|
-
import {Widgets} from "@khanacademy/perseus";
|
|
21
|
-
import AllEditors from "./all-editors";
|
|
22
|
-
import AllWidgets from "./all-widgets";
|
|
23
|
-
|
|
24
|
-
Widgets.registerEditors(AllEditors);
|
|
25
|
-
Widgets.registerWidgets(AllWidgets);
|
|
26
|
-
|
|
27
|
-
Widgets.replaceEditor("transformer", "deprecated-standin");
|
|
28
|
-
Widgets.replaceWidget("transformer", "deprecated-standin");
|
|
29
|
-
|
|
30
|
-
export {AllEditors, AllWidgets};
|
package/src/item-editor.tsx
DELETED
|
@@ -1,158 +0,0 @@
|
|
|
1
|
-
import {itemDataVersion} from "@khanacademy/perseus";
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
import _ from "underscore";
|
|
4
|
-
|
|
5
|
-
import DeviceFramer from "./components/device-framer";
|
|
6
|
-
import Editor from "./editor";
|
|
7
|
-
import IframeContentRenderer from "./iframe-content-renderer";
|
|
8
|
-
import ItemExtrasEditor from "./item-extras-editor";
|
|
9
|
-
|
|
10
|
-
import type {
|
|
11
|
-
APIOptions,
|
|
12
|
-
ImageUploader,
|
|
13
|
-
ChangeHandler,
|
|
14
|
-
} from "@khanacademy/perseus";
|
|
15
|
-
|
|
16
|
-
const ITEM_DATA_VERSION = itemDataVersion;
|
|
17
|
-
|
|
18
|
-
type Props = {
|
|
19
|
-
apiOptions?: APIOptions;
|
|
20
|
-
deviceType?: string;
|
|
21
|
-
gradeMessage?: string;
|
|
22
|
-
imageUploader?: ImageUploader;
|
|
23
|
-
wasAnswered?: boolean;
|
|
24
|
-
question?: any;
|
|
25
|
-
answerArea?: any;
|
|
26
|
-
// URL of the route to show on initial load of the preview frames.
|
|
27
|
-
previewURL: string;
|
|
28
|
-
onChange: ChangeHandler;
|
|
29
|
-
// The content ID of the AssessmentItem being edited. It may not be set
|
|
30
|
-
// for non-content library exercise questions.
|
|
31
|
-
itemId?: string;
|
|
32
|
-
};
|
|
33
|
-
|
|
34
|
-
class ItemEditor extends React.Component<Props> {
|
|
35
|
-
static defaultProps: {
|
|
36
|
-
answerArea: Record<any, any>;
|
|
37
|
-
onChange: () => void;
|
|
38
|
-
question: Record<any, any>;
|
|
39
|
-
} = {
|
|
40
|
-
onChange: () => {},
|
|
41
|
-
question: {},
|
|
42
|
-
answerArea: {},
|
|
43
|
-
};
|
|
44
|
-
|
|
45
|
-
frame = React.createRef<IframeContentRenderer>();
|
|
46
|
-
questionEditor = React.createRef<Editor>();
|
|
47
|
-
itemExtrasEditor = React.createRef<ItemExtrasEditor>();
|
|
48
|
-
|
|
49
|
-
// Notify the parent that the question or answer area has been updated.
|
|
50
|
-
updateProps: ChangeHandler = (newProps, cb, silent) => {
|
|
51
|
-
const props = _(this.props).pick("question", "answerArea");
|
|
52
|
-
|
|
53
|
-
this.props.onChange(_(props).extend(newProps), cb, silent);
|
|
54
|
-
};
|
|
55
|
-
|
|
56
|
-
triggerPreviewUpdate: (newData?: any) => void = (newData: any) => {
|
|
57
|
-
this.frame.current?.sendNewData(newData);
|
|
58
|
-
};
|
|
59
|
-
|
|
60
|
-
handleEditorChange: ChangeHandler = (newProps, cb, silent) => {
|
|
61
|
-
const question = _.extend({}, this.props.question, newProps);
|
|
62
|
-
this.updateProps({question}, cb, silent);
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
handleItemExtrasChange: ChangeHandler = (newProps, cb, silent) => {
|
|
66
|
-
const answerArea = _.extend({}, this.props.answerArea, newProps);
|
|
67
|
-
this.updateProps({answerArea}, cb, silent);
|
|
68
|
-
};
|
|
69
|
-
|
|
70
|
-
getSaveWarnings: () => any = () => {
|
|
71
|
-
return this.questionEditor.current?.getSaveWarnings();
|
|
72
|
-
};
|
|
73
|
-
|
|
74
|
-
serialize: (options?: any) => {
|
|
75
|
-
answerArea: any;
|
|
76
|
-
itemDataVersion: {
|
|
77
|
-
major: number;
|
|
78
|
-
minor: number;
|
|
79
|
-
};
|
|
80
|
-
question: any;
|
|
81
|
-
} = (options: any) => {
|
|
82
|
-
return {
|
|
83
|
-
question: this.questionEditor.current?.serialize(options),
|
|
84
|
-
answerArea: this.itemExtrasEditor.current?.serialize(),
|
|
85
|
-
itemDataVersion: ITEM_DATA_VERSION,
|
|
86
|
-
};
|
|
87
|
-
};
|
|
88
|
-
|
|
89
|
-
render(): React.ReactNode {
|
|
90
|
-
const isMobile =
|
|
91
|
-
this.props.deviceType === "phone" ||
|
|
92
|
-
this.props.deviceType === "tablet";
|
|
93
|
-
return (
|
|
94
|
-
<div className="perseus-editor-table">
|
|
95
|
-
<div className="perseus-editor-row perseus-question-container">
|
|
96
|
-
<div className="perseus-editor-left-cell">
|
|
97
|
-
<div className="pod-title">Question</div>
|
|
98
|
-
<Editor
|
|
99
|
-
ref={this.questionEditor}
|
|
100
|
-
// Using the AssessmentItem content ID as the key
|
|
101
|
-
// ensures that when the user navigates to another
|
|
102
|
-
// item in the Sidebar, the question editor is
|
|
103
|
-
// re-rendered by React.
|
|
104
|
-
key={this.props.itemId}
|
|
105
|
-
placeholder="Type your question here..."
|
|
106
|
-
className="perseus-question-editor"
|
|
107
|
-
imageUploader={this.props.imageUploader}
|
|
108
|
-
onChange={this.handleEditorChange}
|
|
109
|
-
apiOptions={this.props.apiOptions}
|
|
110
|
-
showWordCount={true}
|
|
111
|
-
{...this.props.question}
|
|
112
|
-
/>
|
|
113
|
-
</div>
|
|
114
|
-
|
|
115
|
-
<div className="perseus-editor-right-cell">
|
|
116
|
-
<div id="problemarea">
|
|
117
|
-
<DeviceFramer
|
|
118
|
-
deviceType={this.props.deviceType}
|
|
119
|
-
nochrome={true}
|
|
120
|
-
>
|
|
121
|
-
<IframeContentRenderer
|
|
122
|
-
ref={this.frame}
|
|
123
|
-
key={this.props.deviceType}
|
|
124
|
-
datasetKey="mobile"
|
|
125
|
-
datasetValue={isMobile}
|
|
126
|
-
seamless={true}
|
|
127
|
-
url={this.props.previewURL}
|
|
128
|
-
/>
|
|
129
|
-
</DeviceFramer>
|
|
130
|
-
<div
|
|
131
|
-
id="hintsarea"
|
|
132
|
-
className="hintsarea"
|
|
133
|
-
style={{display: "none"}}
|
|
134
|
-
/>
|
|
135
|
-
</div>
|
|
136
|
-
</div>
|
|
137
|
-
</div>
|
|
138
|
-
|
|
139
|
-
<div className="perseus-editor-row perseus-answer-container">
|
|
140
|
-
<div className="perseus-editor-left-cell">
|
|
141
|
-
<div className="pod-title">Question extras</div>
|
|
142
|
-
<ItemExtrasEditor
|
|
143
|
-
ref={this.itemExtrasEditor}
|
|
144
|
-
onChange={this.handleItemExtrasChange}
|
|
145
|
-
{...this.props.answerArea}
|
|
146
|
-
/>
|
|
147
|
-
</div>
|
|
148
|
-
|
|
149
|
-
<div className="perseus-editor-right-cell">
|
|
150
|
-
<div id="answer_area" />
|
|
151
|
-
</div>
|
|
152
|
-
</div>
|
|
153
|
-
</div>
|
|
154
|
-
);
|
|
155
|
-
}
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
export default ItemEditor;
|
|
@@ -1,230 +0,0 @@
|
|
|
1
|
-
import {components, ItemExtras} from "@khanacademy/perseus";
|
|
2
|
-
import * as React from "react";
|
|
3
|
-
|
|
4
|
-
import type {PerseusAnswerArea} from "@khanacademy/perseus";
|
|
5
|
-
|
|
6
|
-
const {InfoTip} = components;
|
|
7
|
-
|
|
8
|
-
type Props = PerseusAnswerArea & {
|
|
9
|
-
onChange: (props: Partial<PerseusAnswerArea>) => void;
|
|
10
|
-
};
|
|
11
|
-
|
|
12
|
-
type State = {
|
|
13
|
-
financialCalculatorOptionsExpanded: boolean;
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
class ItemExtrasEditor extends React.Component<Props, State> {
|
|
17
|
-
static defaultProps: PerseusAnswerArea = {
|
|
18
|
-
calculator: false,
|
|
19
|
-
chi2Table: false,
|
|
20
|
-
financialCalculatorMonthlyPayment: false,
|
|
21
|
-
financialCalculatorTotalAmount: false,
|
|
22
|
-
financialCalculatorTimeToPayOff: false,
|
|
23
|
-
periodicTable: false,
|
|
24
|
-
periodicTableWithKey: false,
|
|
25
|
-
tTable: false,
|
|
26
|
-
zTable: false,
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
state = {
|
|
30
|
-
financialCalculatorOptionsExpanded: false,
|
|
31
|
-
};
|
|
32
|
-
|
|
33
|
-
componentDidUpdate(prevProps: Readonly<Props>): void {
|
|
34
|
-
// If no financial calculator options are checked, uncheck the
|
|
35
|
-
// financial calculator option.
|
|
36
|
-
if (
|
|
37
|
-
this.state.financialCalculatorOptionsExpanded &&
|
|
38
|
-
!this.props.financialCalculatorMonthlyPayment &&
|
|
39
|
-
!this.props.financialCalculatorTotalAmount &&
|
|
40
|
-
!this.props.financialCalculatorTimeToPayOff
|
|
41
|
-
) {
|
|
42
|
-
this.setState({financialCalculatorOptionsExpanded: false});
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
serialize: () => PerseusAnswerArea = () => {
|
|
47
|
-
const data = {...ItemExtrasEditor.defaultProps};
|
|
48
|
-
for (const key of ItemExtras) {
|
|
49
|
-
data[key] = !!this.props[key];
|
|
50
|
-
}
|
|
51
|
-
return data;
|
|
52
|
-
};
|
|
53
|
-
|
|
54
|
-
render(): React.ReactNode {
|
|
55
|
-
return (
|
|
56
|
-
<div className="perseus-answer-editor">
|
|
57
|
-
<div className="perseus-answer-options">
|
|
58
|
-
<ItemExtraCheckbox
|
|
59
|
-
label="Show calculator:"
|
|
60
|
-
infoTip="Use the calculator when completing difficult calculations is NOT the intent of the question. DON’T use the calculator when testing the student’s ability to complete different types of computations."
|
|
61
|
-
checked={this.props.calculator}
|
|
62
|
-
onChange={(e) => {
|
|
63
|
-
this.props.onChange({
|
|
64
|
-
calculator: e.target.checked,
|
|
65
|
-
});
|
|
66
|
-
}}
|
|
67
|
-
/>
|
|
68
|
-
|
|
69
|
-
<ItemExtraCheckbox
|
|
70
|
-
label="Show financial calculator:"
|
|
71
|
-
infoTip="This provides the student with the ability to view a financial calculator, e.g., for answering financial questions. Once checked, requires at least one of the three options below to be checked."
|
|
72
|
-
checked={this.state.financialCalculatorOptionsExpanded}
|
|
73
|
-
onChange={(e) => {
|
|
74
|
-
this.setState({
|
|
75
|
-
financialCalculatorOptionsExpanded:
|
|
76
|
-
e.target.checked,
|
|
77
|
-
});
|
|
78
|
-
// If the financial calculator is unchecked,
|
|
79
|
-
// this needs to be reset. All checked by
|
|
80
|
-
// default.
|
|
81
|
-
this.props.onChange({
|
|
82
|
-
financialCalculatorMonthlyPayment:
|
|
83
|
-
e.target.checked,
|
|
84
|
-
financialCalculatorTotalAmount:
|
|
85
|
-
e.target.checked,
|
|
86
|
-
financialCalculatorTimeToPayOff:
|
|
87
|
-
e.target.checked,
|
|
88
|
-
});
|
|
89
|
-
}}
|
|
90
|
-
/>
|
|
91
|
-
|
|
92
|
-
{this.state.financialCalculatorOptionsExpanded && (
|
|
93
|
-
<>
|
|
94
|
-
<ItemExtraCheckbox
|
|
95
|
-
label="Include monthly payment:"
|
|
96
|
-
infoTip="This provides the student with the ability to view a monthly payment calculator; e.g., given a loan amount, interest rate, and term, what is the monthly payment?"
|
|
97
|
-
checked={
|
|
98
|
-
this.props.financialCalculatorMonthlyPayment
|
|
99
|
-
}
|
|
100
|
-
onChange={(e) => {
|
|
101
|
-
this.props.onChange({
|
|
102
|
-
financialCalculatorMonthlyPayment:
|
|
103
|
-
e.target.checked,
|
|
104
|
-
});
|
|
105
|
-
}}
|
|
106
|
-
indent
|
|
107
|
-
/>
|
|
108
|
-
<ItemExtraCheckbox
|
|
109
|
-
label="Include total amount:"
|
|
110
|
-
infoTip="This provides the student with the ability to view a total amount calculator; e.g., given a monthly payment, interest rate, and term, what is the total amount to be paid?"
|
|
111
|
-
checked={
|
|
112
|
-
this.props.financialCalculatorTotalAmount
|
|
113
|
-
}
|
|
114
|
-
onChange={(e) => {
|
|
115
|
-
this.props.onChange({
|
|
116
|
-
financialCalculatorTotalAmount:
|
|
117
|
-
e.target.checked,
|
|
118
|
-
});
|
|
119
|
-
}}
|
|
120
|
-
indent
|
|
121
|
-
/>
|
|
122
|
-
<ItemExtraCheckbox
|
|
123
|
-
label="Include time-to-pay-off:"
|
|
124
|
-
infoTip="This provides the student with the ability to view a time to pay off calculator; e.g., given a loan amount, interest rate, and monthly payment, how long will it take to pay off the loan?"
|
|
125
|
-
checked={
|
|
126
|
-
this.props.financialCalculatorTimeToPayOff
|
|
127
|
-
}
|
|
128
|
-
onChange={(e) => {
|
|
129
|
-
this.props.onChange({
|
|
130
|
-
financialCalculatorTimeToPayOff:
|
|
131
|
-
e.target.checked,
|
|
132
|
-
});
|
|
133
|
-
}}
|
|
134
|
-
indent
|
|
135
|
-
/>
|
|
136
|
-
</>
|
|
137
|
-
)}
|
|
138
|
-
|
|
139
|
-
<ItemExtraCheckbox
|
|
140
|
-
label="Show periodic table:"
|
|
141
|
-
infoTip="This provides the student with the ability to view a periodic table of the elements, e.g., for answering chemistry questions."
|
|
142
|
-
checked={this.props.periodicTable}
|
|
143
|
-
onChange={(e) => {
|
|
144
|
-
this.props.onChange({
|
|
145
|
-
periodicTable: e.target.checked,
|
|
146
|
-
// If the periodic table is unchecked,
|
|
147
|
-
// this needs to be reset. If table is
|
|
148
|
-
// checked, it should already be false.
|
|
149
|
-
periodicTableWithKey: false,
|
|
150
|
-
});
|
|
151
|
-
}}
|
|
152
|
-
/>
|
|
153
|
-
|
|
154
|
-
{this.props.periodicTable && (
|
|
155
|
-
<ItemExtraCheckbox
|
|
156
|
-
label="Include key/legend with periodic table:"
|
|
157
|
-
infoTip="Include a key for HS courses; omit for AP chemistry."
|
|
158
|
-
checked={this.props.periodicTableWithKey}
|
|
159
|
-
onChange={(e) => {
|
|
160
|
-
this.props.onChange({
|
|
161
|
-
periodicTableWithKey: e.target.checked,
|
|
162
|
-
});
|
|
163
|
-
}}
|
|
164
|
-
indent
|
|
165
|
-
/>
|
|
166
|
-
)}
|
|
167
|
-
|
|
168
|
-
<ItemExtraCheckbox
|
|
169
|
-
label="Show z table (statistics):"
|
|
170
|
-
infoTip="This provides the student with the ability to view a table of critical values for the z distribution, e.g. for answering statistics questions."
|
|
171
|
-
checked={this.props.zTable}
|
|
172
|
-
onChange={(e) => {
|
|
173
|
-
this.props.onChange({
|
|
174
|
-
zTable: e.target.checked,
|
|
175
|
-
});
|
|
176
|
-
}}
|
|
177
|
-
/>
|
|
178
|
-
|
|
179
|
-
<ItemExtraCheckbox
|
|
180
|
-
label="Show t table (statistics):"
|
|
181
|
-
infoTip="This provides the student with the ability to view a table of critical values for the Student's t distribution, e.g. for answering statistics questions."
|
|
182
|
-
checked={this.props.tTable}
|
|
183
|
-
onChange={(e) => {
|
|
184
|
-
this.props.onChange({
|
|
185
|
-
tTable: e.target.checked,
|
|
186
|
-
});
|
|
187
|
-
}}
|
|
188
|
-
/>
|
|
189
|
-
|
|
190
|
-
<ItemExtraCheckbox
|
|
191
|
-
label="Show chi-squared table (statistics):"
|
|
192
|
-
infoTip="This provides the student with the ability to view a table of critical values for the chi-squared distribution, e.g. for answering statistics questions."
|
|
193
|
-
checked={this.props.chi2Table}
|
|
194
|
-
onChange={(e) => {
|
|
195
|
-
this.props.onChange({
|
|
196
|
-
chi2Table: e.target.checked,
|
|
197
|
-
});
|
|
198
|
-
}}
|
|
199
|
-
/>
|
|
200
|
-
</div>
|
|
201
|
-
</div>
|
|
202
|
-
);
|
|
203
|
-
}
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
const ItemExtraCheckbox = (props: {
|
|
207
|
-
label: string;
|
|
208
|
-
infoTip: string;
|
|
209
|
-
checked: boolean;
|
|
210
|
-
onChange: (e: React.ChangeEvent<HTMLInputElement>) => void;
|
|
211
|
-
indent?: boolean;
|
|
212
|
-
}) => (
|
|
213
|
-
<div
|
|
214
|
-
style={{
|
|
215
|
-
marginLeft: props.indent ? "10px" : "0px",
|
|
216
|
-
}}
|
|
217
|
-
>
|
|
218
|
-
<label>
|
|
219
|
-
{props.label}
|
|
220
|
-
<input
|
|
221
|
-
type="checkbox"
|
|
222
|
-
checked={props.checked}
|
|
223
|
-
onChange={props.onChange}
|
|
224
|
-
/>
|
|
225
|
-
</label>
|
|
226
|
-
<InfoTip>{props.infoTip}</InfoTip>
|
|
227
|
-
</div>
|
|
228
|
-
);
|
|
229
|
-
|
|
230
|
-
export default ItemExtrasEditor;
|
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
/* -*- Mode: Javascript; indent-tabs-mode:nil; js-indent-level: 2 -*- */
|
|
2
|
-
/* vim: set ts=2 et sw=2 tw=80: */
|
|
3
|
-
|
|
4
|
-
/*************************************************************
|
|
5
|
-
*
|
|
6
|
-
* KaTeX mhchem.js
|
|
7
|
-
*
|
|
8
|
-
* This file implements a KaTeX version of mhchem version 3.3.0.
|
|
9
|
-
* It is adapted from MathJax/extensions/TeX/mhchem.js
|
|
10
|
-
* It differs from the MathJax version as follows:
|
|
11
|
-
* 1. The interface is changed so that it can be called from KaTeX, not MathJax.
|
|
12
|
-
* 2. \rlap and \llap are replaced with \mathrlap and \mathllap.
|
|
13
|
-
* 3. Four lines of code are edited in order to use \raisebox instead of \raise.
|
|
14
|
-
* 4. The reaction arrow code is simplified. All reaction arrows are rendered
|
|
15
|
-
* using KaTeX extensible arrows instead of building non-extensible arrows.
|
|
16
|
-
* 5. \tripledash vertical alignment is slightly adjusted.
|
|
17
|
-
*
|
|
18
|
-
* This code, as other KaTeX code, is released under the MIT license.
|
|
19
|
-
*
|
|
20
|
-
* /*************************************************************
|
|
21
|
-
*
|
|
22
|
-
* MathJax/extensions/TeX/mhchem.js
|
|
23
|
-
*
|
|
24
|
-
* Implements the \ce command for handling chemical formulas
|
|
25
|
-
* from the mhchem LaTeX package.
|
|
26
|
-
*
|
|
27
|
-
* ---------------------------------------------------------------------
|
|
28
|
-
*
|
|
29
|
-
* Copyright (c) 2011-2015 The MathJax Consortium
|
|
30
|
-
* Copyright (c) 2015-2018 Martin Hensel
|
|
31
|
-
*
|
|
32
|
-
* Licensed under the Apache License, Version 2.0 (the "License");
|
|
33
|
-
* you may not use this file except in compliance with the License.
|
|
34
|
-
* You may obtain a copy of the License at
|
|
35
|
-
*
|
|
36
|
-
* http://www.apache.org/licenses/LICENSE-2.0
|
|
37
|
-
*
|
|
38
|
-
* Unless required by applicable law or agreed to in writing, software
|
|
39
|
-
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
40
|
-
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
41
|
-
* See the License for the specific language governing permissions and
|
|
42
|
-
* limitations under the License.
|
|
43
|
-
*/
|
|
44
|
-
|
|
45
|
-
//
|
|
46
|
-
// Coding Style
|
|
47
|
-
// - use '' for identifiers that can by minified/uglified
|
|
48
|
-
// - use "" for strings that need to stay untouched
|
|
49
|
-
|
|
50
|
-
// version: "3.3.0" for MathJax and KaTeX
|
|
51
|
-
|
|
52
|
-
import katex from "katex";
|
|
53
|
-
|
|
54
|
-
import {chemParse} from "./parser";
|
|
55
|
-
|
|
56
|
-
// Add \ce, \pu, and \tripledash to the KaTeX macros.
|
|
57
|
-
|
|
58
|
-
katex.__defineMacro("\\ce", function (context) {
|
|
59
|
-
return chemParse(context.consumeArgs(1)[0], "ce");
|
|
60
|
-
});
|
|
61
|
-
|
|
62
|
-
katex.__defineMacro("\\pu", function (context) {
|
|
63
|
-
return chemParse(context.consumeArgs(1)[0], "pu");
|
|
64
|
-
});
|
|
65
|
-
|
|
66
|
-
// Needed for \bond for the ~ forms
|
|
67
|
-
// Raise by 2.56mu, not 2mu. We're raising a hyphen-minus, U+002D, not
|
|
68
|
-
// a mathematical minus, U+2212. So we need that extra 0.56.
|
|
69
|
-
katex.__defineMacro(
|
|
70
|
-
"\\tripledash",
|
|
71
|
-
"{\\vphantom{-}\\raisebox{2.56mu}{$\\mkern2mu" +
|
|
72
|
-
"\\tiny\\text{-}\\mkern1mu\\text{-}\\mkern1mu\\text{-}\\mkern2mu$}}",
|
|
73
|
-
);
|