@pie-lib/math-input-mathlive 1.0.9-next.6618 → 1.0.9
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/README.md +15 -6
- package/lib/__tests__/composites.test.js +255 -0
- package/lib/__tests__/composites.test.js.map +1 -0
- package/lib/__tests__/drop-in-compat.test.js +133 -0
- package/lib/__tests__/drop-in-compat.test.js.map +1 -0
- package/lib/__tests__/index.test.js +118 -0
- package/lib/__tests__/index.test.js.map +1 -0
- package/lib/__tests__/input-imperative.test.js +155 -0
- package/lib/__tests__/input-imperative.test.js.map +1 -0
- package/lib/__tests__/keypad-label.test.js +44 -0
- package/lib/__tests__/keypad-label.test.js.map +1 -0
- package/lib/__tests__/keypad-render.test.js +206 -0
- package/lib/__tests__/keypad-render.test.js.map +1 -0
- package/lib/__tests__/latex-bridge.test.js +251 -0
- package/lib/__tests__/latex-bridge.test.js.map +1 -0
- package/lib/__tests__/mathlive-instance.test.js +237 -0
- package/lib/__tests__/mathlive-instance.test.js.map +1 -0
- package/lib/__tests__/mount-race.test.js +194 -0
- package/lib/__tests__/mount-race.test.js.map +1 -0
- package/lib/__tests__/static-prompts.test.js +282 -0
- package/lib/__tests__/static-prompts.test.js.map +1 -0
- package/lib/mathlive-instance.js +47 -10
- package/lib/mathlive-instance.js.map +1 -1
- package/lib/mf/input.js +18 -5
- package/lib/mf/input.js.map +1 -1
- package/lib/mf/static.js +17 -4
- package/lib/mf/static.js.map +1 -1
- package/package.json +3 -4
- package/src/__tests__/composites.test.jsx +220 -0
- package/src/__tests__/keypad-label.test.jsx +5 -13
- package/src/__tests__/keypad-render.test.jsx +123 -0
- package/src/__tests__/mathlive-instance.test.js +40 -0
- package/src/__tests__/mount-race.test.jsx +129 -0
- package/src/__tests__/static-prompts.test.jsx +251 -0
- package/src/mathlive-instance.js +50 -11
- package/src/mf/input.jsx +19 -5
- package/src/mf/static.jsx +20 -4
- package/LICENSE.md +0 -5
|
@@ -0,0 +1,194 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _regenerator = _interopRequireDefault(require("@babel/runtime/regenerator"));
|
|
5
|
+
var _asyncToGenerator2 = _interopRequireDefault(require("@babel/runtime/helpers/asyncToGenerator"));
|
|
6
|
+
var _createClass2 = _interopRequireDefault(require("@babel/runtime/helpers/createClass"));
|
|
7
|
+
var _classCallCheck2 = _interopRequireDefault(require("@babel/runtime/helpers/classCallCheck"));
|
|
8
|
+
var _input = _interopRequireDefault(require("../mf/input"));
|
|
9
|
+
/**
|
|
10
|
+
* MathLive loads asynchronously, so `componentDidMount` resumes after an await
|
|
11
|
+
* before appending a <math-field>. Two orderings must both hold:
|
|
12
|
+
*
|
|
13
|
+
* - StrictMode dev double-mount (mount -> unmount -> mount on the SAME
|
|
14
|
+
* instance) must end up with exactly ONE field. A boolean "unmounted" flag
|
|
15
|
+
* latches here and blocks the remount entirely - which is why this uses a
|
|
16
|
+
* generation counter.
|
|
17
|
+
* - A genuine unmount while the load is in flight must append nothing.
|
|
18
|
+
*
|
|
19
|
+
* MathLive cannot load under jsdom, so it is mocked with a minimal stub in
|
|
20
|
+
* order to actually count appended fields.
|
|
21
|
+
*/
|
|
22
|
+
jest.mock('../mathlive-instance', function () {
|
|
23
|
+
// Must be a real DOM node: the component uses replaceChildren(), which
|
|
24
|
+
// rejects anything that is not a Node. Returning an element from the
|
|
25
|
+
// constructor replaces `this`, so callers get a genuine <math-field>.
|
|
26
|
+
var FakeMathfieldElement = /*#__PURE__*/(0, _createClass2["default"])(function FakeMathfieldElement() {
|
|
27
|
+
(0, _classCallCheck2["default"])(this, FakeMathfieldElement);
|
|
28
|
+
// `global` rather than `document`: jest forbids out-of-scope variables in
|
|
29
|
+
// a mock factory, and `global` is on its allow-list.
|
|
30
|
+
var el = global.document.createElement('math-field');
|
|
31
|
+
el.value = '';
|
|
32
|
+
el.getValue = function () {
|
|
33
|
+
return el.value;
|
|
34
|
+
};
|
|
35
|
+
return el;
|
|
36
|
+
});
|
|
37
|
+
return {
|
|
38
|
+
loadMathLive: function loadMathLive() {
|
|
39
|
+
return Promise.resolve({
|
|
40
|
+
MathfieldElement: FakeMathfieldElement
|
|
41
|
+
});
|
|
42
|
+
},
|
|
43
|
+
getMacros: function getMacros() {
|
|
44
|
+
return {};
|
|
45
|
+
},
|
|
46
|
+
// must mirror the real export, otherwise the component sets `undefined`
|
|
47
|
+
MATH_MODE_SPACE: '\\ ',
|
|
48
|
+
latexToMarkup: function latexToMarkup(l) {
|
|
49
|
+
return "<span>".concat(l, "</span>");
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
// eslint-disable-next-line import/first
|
|
55
|
+
|
|
56
|
+
var holderFor = function holderFor(cmp) {
|
|
57
|
+
var el = document.createElement('span');
|
|
58
|
+
cmp.holderRef = {
|
|
59
|
+
current: el
|
|
60
|
+
};
|
|
61
|
+
return el;
|
|
62
|
+
};
|
|
63
|
+
var fieldCount = function fieldCount(el) {
|
|
64
|
+
return Array.from(el.childNodes).filter(function (n) {
|
|
65
|
+
return n.tagName === 'MATH-FIELD';
|
|
66
|
+
}).length;
|
|
67
|
+
};
|
|
68
|
+
describe('async mount race', function () {
|
|
69
|
+
it('StrictMode mount -> unmount -> mount yields exactly one field', /*#__PURE__*/(0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee() {
|
|
70
|
+
var input, el, first, second;
|
|
71
|
+
return _regenerator["default"].wrap(function (_context) {
|
|
72
|
+
while (1) switch (_context.prev = _context.next) {
|
|
73
|
+
case 0:
|
|
74
|
+
input = new _input["default"]({});
|
|
75
|
+
el = holderFor(input);
|
|
76
|
+
first = input.componentDidMount();
|
|
77
|
+
input.componentWillUnmount();
|
|
78
|
+
second = input.componentDidMount();
|
|
79
|
+
_context.next = 1;
|
|
80
|
+
return Promise.all([first, second]);
|
|
81
|
+
case 1:
|
|
82
|
+
expect(fieldCount(el)).toBe(1);
|
|
83
|
+
expect(input.mathField).toBeDefined();
|
|
84
|
+
case 2:
|
|
85
|
+
case "end":
|
|
86
|
+
return _context.stop();
|
|
87
|
+
}
|
|
88
|
+
}, _callee);
|
|
89
|
+
})));
|
|
90
|
+
it('appends nothing when unmounted while the load is in flight', /*#__PURE__*/(0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee2() {
|
|
91
|
+
var input, el, pending;
|
|
92
|
+
return _regenerator["default"].wrap(function (_context2) {
|
|
93
|
+
while (1) switch (_context2.prev = _context2.next) {
|
|
94
|
+
case 0:
|
|
95
|
+
input = new _input["default"]({});
|
|
96
|
+
el = holderFor(input);
|
|
97
|
+
pending = input.componentDidMount();
|
|
98
|
+
input.componentWillUnmount();
|
|
99
|
+
_context2.next = 1;
|
|
100
|
+
return pending;
|
|
101
|
+
case 1:
|
|
102
|
+
expect(fieldCount(el)).toBe(0);
|
|
103
|
+
expect(input.mathField).toBeUndefined();
|
|
104
|
+
case 2:
|
|
105
|
+
case "end":
|
|
106
|
+
return _context2.stop();
|
|
107
|
+
}
|
|
108
|
+
}, _callee2);
|
|
109
|
+
})));
|
|
110
|
+
it('a normal single mount creates the field', /*#__PURE__*/(0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee3() {
|
|
111
|
+
var input, el;
|
|
112
|
+
return _regenerator["default"].wrap(function (_context3) {
|
|
113
|
+
while (1) switch (_context3.prev = _context3.next) {
|
|
114
|
+
case 0:
|
|
115
|
+
input = new _input["default"]({});
|
|
116
|
+
el = holderFor(input);
|
|
117
|
+
_context3.next = 1;
|
|
118
|
+
return input.componentDidMount();
|
|
119
|
+
case 1:
|
|
120
|
+
expect(fieldCount(el)).toBe(1);
|
|
121
|
+
case 2:
|
|
122
|
+
case "end":
|
|
123
|
+
return _context3.stop();
|
|
124
|
+
}
|
|
125
|
+
}, _callee3);
|
|
126
|
+
})));
|
|
127
|
+
it('repeated mounts never accumulate fields', /*#__PURE__*/(0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee4() {
|
|
128
|
+
var input, el;
|
|
129
|
+
return _regenerator["default"].wrap(function (_context4) {
|
|
130
|
+
while (1) switch (_context4.prev = _context4.next) {
|
|
131
|
+
case 0:
|
|
132
|
+
input = new _input["default"]({});
|
|
133
|
+
el = holderFor(input);
|
|
134
|
+
_context4.next = 1;
|
|
135
|
+
return input.componentDidMount();
|
|
136
|
+
case 1:
|
|
137
|
+
_context4.next = 2;
|
|
138
|
+
return input.componentDidMount();
|
|
139
|
+
case 2:
|
|
140
|
+
_context4.next = 3;
|
|
141
|
+
return input.componentDidMount();
|
|
142
|
+
case 3:
|
|
143
|
+
expect(fieldCount(el)).toBe(1);
|
|
144
|
+
case 4:
|
|
145
|
+
case "end":
|
|
146
|
+
return _context4.stop();
|
|
147
|
+
}
|
|
148
|
+
}, _callee4);
|
|
149
|
+
})));
|
|
150
|
+
|
|
151
|
+
// MathLive's mathModeSpace defaults to '', which makes the spacebar a no-op.
|
|
152
|
+
it('configures the spacebar so a space can be typed', /*#__PURE__*/(0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee5() {
|
|
153
|
+
var input;
|
|
154
|
+
return _regenerator["default"].wrap(function (_context5) {
|
|
155
|
+
while (1) switch (_context5.prev = _context5.next) {
|
|
156
|
+
case 0:
|
|
157
|
+
input = new _input["default"]({});
|
|
158
|
+
holderFor(input);
|
|
159
|
+
_context5.next = 1;
|
|
160
|
+
return input.componentDidMount();
|
|
161
|
+
case 1:
|
|
162
|
+
// a LaTeX control space - what MathQuill produced, and MathJax understands
|
|
163
|
+
expect(input.mathField.mathModeSpace).toEqual('\\ ');
|
|
164
|
+
expect(input.mathField.mathModeSpace).not.toEqual('');
|
|
165
|
+
case 2:
|
|
166
|
+
case "end":
|
|
167
|
+
return _context5.stop();
|
|
168
|
+
}
|
|
169
|
+
}, _callee5);
|
|
170
|
+
})));
|
|
171
|
+
it('reports itself through innerRef on mount and clears it on unmount', /*#__PURE__*/(0, _asyncToGenerator2["default"])(/*#__PURE__*/_regenerator["default"].mark(function _callee6() {
|
|
172
|
+
var innerRef, input;
|
|
173
|
+
return _regenerator["default"].wrap(function (_context6) {
|
|
174
|
+
while (1) switch (_context6.prev = _context6.next) {
|
|
175
|
+
case 0:
|
|
176
|
+
innerRef = jest.fn();
|
|
177
|
+
input = new _input["default"]({
|
|
178
|
+
innerRef: innerRef
|
|
179
|
+
});
|
|
180
|
+
holderFor(input);
|
|
181
|
+
_context6.next = 1;
|
|
182
|
+
return input.componentDidMount();
|
|
183
|
+
case 1:
|
|
184
|
+
expect(innerRef).toHaveBeenCalledWith(input);
|
|
185
|
+
input.componentWillUnmount();
|
|
186
|
+
expect(innerRef).toHaveBeenLastCalledWith(null);
|
|
187
|
+
case 2:
|
|
188
|
+
case "end":
|
|
189
|
+
return _context6.stop();
|
|
190
|
+
}
|
|
191
|
+
}, _callee6);
|
|
192
|
+
})));
|
|
193
|
+
});
|
|
194
|
+
//# sourceMappingURL=mount-race.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mount-race.test.js","names":["_input","_interopRequireDefault","require","jest","mock","FakeMathfieldElement","_createClass2","_classCallCheck2","el","global","document","createElement","value","getValue","loadMathLive","Promise","resolve","MathfieldElement","getMacros","MATH_MODE_SPACE","latexToMarkup","l","concat","holderFor","cmp","holderRef","current","fieldCount","Array","from","childNodes","filter","n","tagName","length","describe","it","_asyncToGenerator2","_regenerator","mark","_callee","input","first","second","wrap","_context","prev","next","Input","componentDidMount","componentWillUnmount","all","expect","toBe","mathField","toBeDefined","stop","_callee2","pending","_context2","toBeUndefined","_callee3","_context3","_callee4","_context4","_callee5","_context5","mathModeSpace","toEqual","not","_callee6","innerRef","_context6","fn","toHaveBeenCalledWith","toHaveBeenLastCalledWith"],"sources":["../../src/__tests__/mount-race.test.jsx"],"sourcesContent":["/**\n * MathLive loads asynchronously, so `componentDidMount` resumes after an await\n * before appending a <math-field>. Two orderings must both hold:\n *\n * - StrictMode dev double-mount (mount -> unmount -> mount on the SAME\n * instance) must end up with exactly ONE field. A boolean \"unmounted\" flag\n * latches here and blocks the remount entirely - which is why this uses a\n * generation counter.\n * - A genuine unmount while the load is in flight must append nothing.\n *\n * MathLive cannot load under jsdom, so it is mocked with a minimal stub in\n * order to actually count appended fields.\n */\njest.mock('../mathlive-instance', () => {\n // Must be a real DOM node: the component uses replaceChildren(), which\n // rejects anything that is not a Node. Returning an element from the\n // constructor replaces `this`, so callers get a genuine <math-field>.\n class FakeMathfieldElement {\n constructor() {\n // `global` rather than `document`: jest forbids out-of-scope variables in\n // a mock factory, and `global` is on its allow-list.\n const el = global.document.createElement('math-field');\n\n el.value = '';\n el.getValue = () => el.value;\n\n return el;\n }\n }\n\n return {\n loadMathLive: () => Promise.resolve({ MathfieldElement: FakeMathfieldElement }),\n getMacros: () => ({}),\n // must mirror the real export, otherwise the component sets `undefined`\n MATH_MODE_SPACE: '\\\\ ',\n latexToMarkup: (l) => `<span>${l}</span>`,\n };\n});\n\n// eslint-disable-next-line import/first\nimport Input from '../mf/input';\n\nconst holderFor = (cmp) => {\n const el = document.createElement('span');\n\n cmp.holderRef = { current: el };\n\n return el;\n};\n\nconst fieldCount = (el) => Array.from(el.childNodes).filter((n) => n.tagName === 'MATH-FIELD').length;\n\ndescribe('async mount race', () => {\n it('StrictMode mount -> unmount -> mount yields exactly one field', async () => {\n const input = new Input({});\n const el = holderFor(input);\n\n const first = input.componentDidMount();\n\n input.componentWillUnmount();\n\n const second = input.componentDidMount();\n\n await Promise.all([first, second]);\n\n expect(fieldCount(el)).toBe(1);\n expect(input.mathField).toBeDefined();\n });\n\n it('appends nothing when unmounted while the load is in flight', async () => {\n const input = new Input({});\n const el = holderFor(input);\n\n const pending = input.componentDidMount();\n\n input.componentWillUnmount();\n\n await pending;\n\n expect(fieldCount(el)).toBe(0);\n expect(input.mathField).toBeUndefined();\n });\n\n it('a normal single mount creates the field', async () => {\n const input = new Input({});\n const el = holderFor(input);\n\n await input.componentDidMount();\n\n expect(fieldCount(el)).toBe(1);\n });\n\n it('repeated mounts never accumulate fields', async () => {\n const input = new Input({});\n const el = holderFor(input);\n\n await input.componentDidMount();\n await input.componentDidMount();\n await input.componentDidMount();\n\n expect(fieldCount(el)).toBe(1);\n });\n\n // MathLive's mathModeSpace defaults to '', which makes the spacebar a no-op.\n it('configures the spacebar so a space can be typed', async () => {\n const input = new Input({});\n\n holderFor(input);\n await input.componentDidMount();\n\n // a LaTeX control space - what MathQuill produced, and MathJax understands\n expect(input.mathField.mathModeSpace).toEqual('\\\\ ');\n expect(input.mathField.mathModeSpace).not.toEqual('');\n });\n\n it('reports itself through innerRef on mount and clears it on unmount', async () => {\n const innerRef = jest.fn();\n const input = new Input({ innerRef });\n\n holderFor(input);\n await input.componentDidMount();\n\n expect(innerRef).toHaveBeenCalledWith(input);\n\n input.componentWillUnmount();\n\n expect(innerRef).toHaveBeenLastCalledWith(null);\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
|
|
3
|
+
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
|
|
4
|
+
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
|
|
5
|
+
var _static = _interopRequireDefault(require("../mf/static"));
|
|
6
|
+
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
|
|
7
|
+
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2["default"])(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
|
|
8
|
+
/**
|
|
9
|
+
* The answer-block contract: `\MathQuillMathField[id]{}` in stored latex becomes
|
|
10
|
+
* a MathLive prompt, and sub-field values/focus are surfaced through the same
|
|
11
|
+
* callbacks @pie-lib/math-input exposed (`onSubFieldChange`, `onSubFieldFocus`,
|
|
12
|
+
* `getFieldName`, `setInput`).
|
|
13
|
+
*
|
|
14
|
+
* Driven against a stub mathfield: a real <math-field> needs a browser, and what
|
|
15
|
+
* matters here is the mapping, not MathLive's rendering.
|
|
16
|
+
*/
|
|
17
|
+
var stubField = function stubField() {
|
|
18
|
+
var values = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
19
|
+
var selection = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : null;
|
|
20
|
+
return {
|
|
21
|
+
values: _objectSpread({}, values),
|
|
22
|
+
getPrompts: function getPrompts() {
|
|
23
|
+
return Object.keys(this.values);
|
|
24
|
+
},
|
|
25
|
+
getPromptValue: function getPromptValue(id) {
|
|
26
|
+
return this.values[id];
|
|
27
|
+
},
|
|
28
|
+
setPromptValue: function setPromptValue(id, v) {
|
|
29
|
+
this.values[id] = v;
|
|
30
|
+
},
|
|
31
|
+
getPromptRange: function getPromptRange(id) {
|
|
32
|
+
var i = Object.keys(this.values).indexOf(id);
|
|
33
|
+
return [i * 10, i * 10 + 5];
|
|
34
|
+
},
|
|
35
|
+
selection: selection,
|
|
36
|
+
addEventListener: function addEventListener() {},
|
|
37
|
+
removeEventListener: function removeEventListener() {},
|
|
38
|
+
focus: function focus() {
|
|
39
|
+
this.focused = true;
|
|
40
|
+
},
|
|
41
|
+
blur: function blur() {
|
|
42
|
+
this.blurred = true;
|
|
43
|
+
},
|
|
44
|
+
remove: function remove() {}
|
|
45
|
+
};
|
|
46
|
+
};
|
|
47
|
+
var make = function make() {
|
|
48
|
+
var props = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
|
|
49
|
+
return new _static["default"](_objectSpread({
|
|
50
|
+
latex: 'x=\\MathQuillMathField[r1]{}',
|
|
51
|
+
getFieldName: function getFieldName() {}
|
|
52
|
+
}, props));
|
|
53
|
+
};
|
|
54
|
+
describe('Static: answer blocks / prompts', function () {
|
|
55
|
+
describe('isInteractive', function () {
|
|
56
|
+
it('is interactive when the latex has answer blocks', function () {
|
|
57
|
+
expect(make({
|
|
58
|
+
latex: 'x=\\MathQuillMathField[r1]{}'
|
|
59
|
+
}).isInteractive()).toBe(true);
|
|
60
|
+
expect(make({
|
|
61
|
+
latex: 'x=\\placeholder[r1]{}'
|
|
62
|
+
}).isInteractive()).toBe(true);
|
|
63
|
+
});
|
|
64
|
+
it('is display-only for plain latex', function () {
|
|
65
|
+
expect(make({
|
|
66
|
+
latex: '\\frac{1}{2}'
|
|
67
|
+
}).isInteractive()).toBe(false);
|
|
68
|
+
expect(make({
|
|
69
|
+
latex: ''
|
|
70
|
+
}).isInteractive()).toBe(false);
|
|
71
|
+
});
|
|
72
|
+
});
|
|
73
|
+
describe('values', function () {
|
|
74
|
+
it('returns {} before the field exists', function () {
|
|
75
|
+
expect(make().values()).toEqual({});
|
|
76
|
+
});
|
|
77
|
+
it('maps each prompt id to its stored-form latex', function () {
|
|
78
|
+
var s = make();
|
|
79
|
+
s.mathField = stubField({
|
|
80
|
+
r1: '7',
|
|
81
|
+
r2: '\\placeholder{}'
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
// \placeholder must not leak into stored values
|
|
85
|
+
expect(s.values()).toEqual({
|
|
86
|
+
r1: '7',
|
|
87
|
+
r2: ''
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
describe('promptHandle / innerFields', function () {
|
|
92
|
+
it('exposes a MathQuill-field-like handle with get/set latex', function () {
|
|
93
|
+
var s = make();
|
|
94
|
+
s.mathField = stubField({
|
|
95
|
+
r1: '7'
|
|
96
|
+
});
|
|
97
|
+
var h = s.promptHandle('r1');
|
|
98
|
+
expect(h.id).toEqual('r1');
|
|
99
|
+
expect(h.latex()).toEqual('7');
|
|
100
|
+
h.latex('9');
|
|
101
|
+
expect(s.mathField.values.r1).toEqual('9');
|
|
102
|
+
});
|
|
103
|
+
it('handle latex() is safe with no field', function () {
|
|
104
|
+
expect(make().promptHandle('r1').latex()).toEqual('');
|
|
105
|
+
});
|
|
106
|
+
it('innerFields returns one handle per prompt', function () {
|
|
107
|
+
var s = make();
|
|
108
|
+
s.mathField = stubField({
|
|
109
|
+
r1: '1',
|
|
110
|
+
r2: '2'
|
|
111
|
+
});
|
|
112
|
+
expect(s.innerFields.map(function (f) {
|
|
113
|
+
return f.id;
|
|
114
|
+
})).toEqual(['r1', 'r2']);
|
|
115
|
+
expect(make().innerFields).toEqual([]);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
describe('onSubFieldChange', function () {
|
|
119
|
+
it('fires only for prompts whose value changed', function () {
|
|
120
|
+
var onSubFieldChange = jest.fn();
|
|
121
|
+
var s = make({
|
|
122
|
+
onSubFieldChange: onSubFieldChange
|
|
123
|
+
});
|
|
124
|
+
s.mathField = stubField({
|
|
125
|
+
r1: '1',
|
|
126
|
+
r2: '2'
|
|
127
|
+
});
|
|
128
|
+
s.promptValues = {
|
|
129
|
+
r1: '1',
|
|
130
|
+
r2: '2'
|
|
131
|
+
};
|
|
132
|
+
s.mathField.values.r2 = '5';
|
|
133
|
+
s.onPromptInput();
|
|
134
|
+
expect(onSubFieldChange).toHaveBeenCalledTimes(1);
|
|
135
|
+
expect(onSubFieldChange).toHaveBeenCalledWith('r2', '5');
|
|
136
|
+
});
|
|
137
|
+
it('uses getFieldName when it returns a name', function () {
|
|
138
|
+
var onSubFieldChange = jest.fn();
|
|
139
|
+
var s = make({
|
|
140
|
+
onSubFieldChange: onSubFieldChange,
|
|
141
|
+
getFieldName: function getFieldName(handle) {
|
|
142
|
+
return 'named-' + handle.id;
|
|
143
|
+
}
|
|
144
|
+
});
|
|
145
|
+
s.mathField = stubField({
|
|
146
|
+
r1: '1'
|
|
147
|
+
});
|
|
148
|
+
s.promptValues = {};
|
|
149
|
+
s.onPromptInput();
|
|
150
|
+
expect(onSubFieldChange).toHaveBeenCalledWith('named-r1', '1');
|
|
151
|
+
});
|
|
152
|
+
it('still tracks values when no callback is supplied', function () {
|
|
153
|
+
var s = make();
|
|
154
|
+
s.mathField = stubField({
|
|
155
|
+
r1: '3'
|
|
156
|
+
});
|
|
157
|
+
s.onPromptInput();
|
|
158
|
+
expect(s.promptValues).toEqual({
|
|
159
|
+
r1: '3'
|
|
160
|
+
});
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
describe('onSubFieldFocus', function () {
|
|
164
|
+
it('reports the prompt containing the selection, plus setInput', function () {
|
|
165
|
+
var onSubFieldFocus = jest.fn();
|
|
166
|
+
var setInput = jest.fn();
|
|
167
|
+
var s = make({
|
|
168
|
+
onSubFieldFocus: onSubFieldFocus,
|
|
169
|
+
setInput: setInput
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
// r2 occupies offsets 10..15
|
|
173
|
+
s.mathField = stubField({
|
|
174
|
+
r1: '',
|
|
175
|
+
r2: ''
|
|
176
|
+
}, {
|
|
177
|
+
ranges: [[11, 11]]
|
|
178
|
+
});
|
|
179
|
+
s.onPromptFocus();
|
|
180
|
+
expect(onSubFieldFocus).toHaveBeenCalledTimes(1);
|
|
181
|
+
expect(onSubFieldFocus.mock.calls[0][0]).toEqual('r2');
|
|
182
|
+
expect(onSubFieldFocus.mock.calls[0][1].id).toEqual('r2');
|
|
183
|
+
expect(setInput).toHaveBeenCalled();
|
|
184
|
+
});
|
|
185
|
+
it('does nothing without a selection', function () {
|
|
186
|
+
var onSubFieldFocus = jest.fn();
|
|
187
|
+
var s = make({
|
|
188
|
+
onSubFieldFocus: onSubFieldFocus
|
|
189
|
+
});
|
|
190
|
+
s.mathField = stubField({
|
|
191
|
+
r1: ''
|
|
192
|
+
}, null);
|
|
193
|
+
s.onPromptFocus();
|
|
194
|
+
expect(onSubFieldFocus).not.toHaveBeenCalled();
|
|
195
|
+
});
|
|
196
|
+
it('does not throw when the field is missing', function () {
|
|
197
|
+
var s = make({
|
|
198
|
+
onSubFieldFocus: jest.fn()
|
|
199
|
+
});
|
|
200
|
+
expect(function () {
|
|
201
|
+
return s.onPromptFocus();
|
|
202
|
+
}).not.toThrow();
|
|
203
|
+
});
|
|
204
|
+
});
|
|
205
|
+
describe('accessibility announcements', function () {
|
|
206
|
+
afterEach(function () {
|
|
207
|
+
document.body.innerHTML = '';
|
|
208
|
+
});
|
|
209
|
+
it('creates a polite off-screen live region', function () {
|
|
210
|
+
var s = make();
|
|
211
|
+
s.createLiveRegion();
|
|
212
|
+
expect(s.liveRegion.getAttribute('aria-live')).toEqual('polite');
|
|
213
|
+
expect(s.liveRegion.getAttribute('aria-atomic')).toEqual('true');
|
|
214
|
+
expect(document.body.contains(s.liveRegion)).toBe(true);
|
|
215
|
+
s.removeLiveRegion();
|
|
216
|
+
expect(s.liveRegion).toBeUndefined();
|
|
217
|
+
});
|
|
218
|
+
it('does not create a second region', function () {
|
|
219
|
+
var s = make();
|
|
220
|
+
s.createLiveRegion();
|
|
221
|
+
var first = s.liveRegion;
|
|
222
|
+
s.createLiveRegion();
|
|
223
|
+
expect(s.liveRegion).toBe(first);
|
|
224
|
+
s.removeLiveRegion();
|
|
225
|
+
});
|
|
226
|
+
it('announces when typed input expands into a command', function () {
|
|
227
|
+
var s = make();
|
|
228
|
+
s.createLiveRegion();
|
|
229
|
+
s.promptValues = {
|
|
230
|
+
r1: 'x'
|
|
231
|
+
};
|
|
232
|
+
// grew by more than one char AND gained a backslash => a conversion
|
|
233
|
+
s.announceConversion({
|
|
234
|
+
r1: 'x\\frac{}{}'
|
|
235
|
+
});
|
|
236
|
+
expect(s.liveRegion.textContent).toEqual('Converted to math symbol');
|
|
237
|
+
s.removeLiveRegion();
|
|
238
|
+
});
|
|
239
|
+
it('does not announce ordinary typing', function () {
|
|
240
|
+
var s = make();
|
|
241
|
+
s.createLiveRegion();
|
|
242
|
+
s.promptValues = {
|
|
243
|
+
r1: 'x'
|
|
244
|
+
};
|
|
245
|
+
s.announceConversion({
|
|
246
|
+
r1: 'xy'
|
|
247
|
+
});
|
|
248
|
+
expect(s.liveRegion.textContent).toEqual('');
|
|
249
|
+
s.removeLiveRegion();
|
|
250
|
+
});
|
|
251
|
+
it('does not announce a deletion', function () {
|
|
252
|
+
var s = make();
|
|
253
|
+
s.createLiveRegion();
|
|
254
|
+
s.promptValues = {
|
|
255
|
+
r1: 'x\\frac{}{}'
|
|
256
|
+
};
|
|
257
|
+
s.onKeyDown({
|
|
258
|
+
key: 'Backspace'
|
|
259
|
+
});
|
|
260
|
+
s.announceConversion({
|
|
261
|
+
r1: 'x'
|
|
262
|
+
});
|
|
263
|
+
expect(s.liveRegion.textContent).toEqual('');
|
|
264
|
+
s.removeLiveRegion();
|
|
265
|
+
});
|
|
266
|
+
});
|
|
267
|
+
describe('focus / blur', function () {
|
|
268
|
+
it('delegates to the field and is safe without one', function () {
|
|
269
|
+
var s = make();
|
|
270
|
+
expect(function () {
|
|
271
|
+
s.focus();
|
|
272
|
+
s.blur();
|
|
273
|
+
}).not.toThrow();
|
|
274
|
+
s.mathField = stubField();
|
|
275
|
+
s.focus();
|
|
276
|
+
s.blur();
|
|
277
|
+
expect(s.mathField.focused).toBe(true);
|
|
278
|
+
expect(s.mathField.blurred).toBe(true);
|
|
279
|
+
});
|
|
280
|
+
});
|
|
281
|
+
});
|
|
282
|
+
//# sourceMappingURL=static-prompts.test.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"static-prompts.test.js","names":["_static","_interopRequireDefault","require","ownKeys","e","r","t","Object","keys","getOwnPropertySymbols","o","filter","getOwnPropertyDescriptor","enumerable","push","apply","_objectSpread","arguments","length","forEach","_defineProperty2","getOwnPropertyDescriptors","defineProperties","defineProperty","stubField","values","undefined","selection","getPrompts","getPromptValue","id","setPromptValue","v","getPromptRange","i","indexOf","addEventListener","removeEventListener","focus","focused","blur","blurred","remove","make","props","Static","latex","getFieldName","describe","it","expect","isInteractive","toBe","toEqual","s","mathField","r1","r2","h","promptHandle","innerFields","map","f","onSubFieldChange","jest","fn","promptValues","onPromptInput","toHaveBeenCalledTimes","toHaveBeenCalledWith","handle","onSubFieldFocus","setInput","ranges","onPromptFocus","mock","calls","toHaveBeenCalled","not","toThrow","afterEach","document","body","innerHTML","createLiveRegion","liveRegion","getAttribute","contains","removeLiveRegion","toBeUndefined","first","announceConversion","textContent","onKeyDown","key"],"sources":["../../src/__tests__/static-prompts.test.jsx"],"sourcesContent":["import Static from '../mf/static';\n\n/**\n * The answer-block contract: `\\MathQuillMathField[id]{}` in stored latex becomes\n * a MathLive prompt, and sub-field values/focus are surfaced through the same\n * callbacks @pie-lib/math-input exposed (`onSubFieldChange`, `onSubFieldFocus`,\n * `getFieldName`, `setInput`).\n *\n * Driven against a stub mathfield: a real <math-field> needs a browser, and what\n * matters here is the mapping, not MathLive's rendering.\n */\nconst stubField = (values = {}, selection = null) => ({\n values: { ...values },\n getPrompts() {\n return Object.keys(this.values);\n },\n getPromptValue(id) {\n return this.values[id];\n },\n setPromptValue(id, v) {\n this.values[id] = v;\n },\n getPromptRange(id) {\n const i = Object.keys(this.values).indexOf(id);\n\n return [i * 10, i * 10 + 5];\n },\n selection,\n addEventListener() {},\n removeEventListener() {},\n focus() {\n this.focused = true;\n },\n blur() {\n this.blurred = true;\n },\n remove() {},\n});\n\nconst make = (props = {}) => new Static({ latex: 'x=\\\\MathQuillMathField[r1]{}', getFieldName: () => {}, ...props });\n\ndescribe('Static: answer blocks / prompts', () => {\n describe('isInteractive', () => {\n it('is interactive when the latex has answer blocks', () => {\n expect(make({ latex: 'x=\\\\MathQuillMathField[r1]{}' }).isInteractive()).toBe(true);\n expect(make({ latex: 'x=\\\\placeholder[r1]{}' }).isInteractive()).toBe(true);\n });\n\n it('is display-only for plain latex', () => {\n expect(make({ latex: '\\\\frac{1}{2}' }).isInteractive()).toBe(false);\n expect(make({ latex: '' }).isInteractive()).toBe(false);\n });\n });\n\n describe('values', () => {\n it('returns {} before the field exists', () => {\n expect(make().values()).toEqual({});\n });\n\n it('maps each prompt id to its stored-form latex', () => {\n const s = make();\n\n s.mathField = stubField({ r1: '7', r2: '\\\\placeholder{}' });\n\n // \\placeholder must not leak into stored values\n expect(s.values()).toEqual({ r1: '7', r2: '' });\n });\n });\n\n describe('promptHandle / innerFields', () => {\n it('exposes a MathQuill-field-like handle with get/set latex', () => {\n const s = make();\n\n s.mathField = stubField({ r1: '7' });\n\n const h = s.promptHandle('r1');\n\n expect(h.id).toEqual('r1');\n expect(h.latex()).toEqual('7');\n\n h.latex('9');\n expect(s.mathField.values.r1).toEqual('9');\n });\n\n it('handle latex() is safe with no field', () => {\n expect(make().promptHandle('r1').latex()).toEqual('');\n });\n\n it('innerFields returns one handle per prompt', () => {\n const s = make();\n\n s.mathField = stubField({ r1: '1', r2: '2' });\n\n expect(s.innerFields.map((f) => f.id)).toEqual(['r1', 'r2']);\n expect(make().innerFields).toEqual([]);\n });\n });\n\n describe('onSubFieldChange', () => {\n it('fires only for prompts whose value changed', () => {\n const onSubFieldChange = jest.fn();\n const s = make({ onSubFieldChange });\n\n s.mathField = stubField({ r1: '1', r2: '2' });\n s.promptValues = { r1: '1', r2: '2' };\n\n s.mathField.values.r2 = '5';\n s.onPromptInput();\n\n expect(onSubFieldChange).toHaveBeenCalledTimes(1);\n expect(onSubFieldChange).toHaveBeenCalledWith('r2', '5');\n });\n\n it('uses getFieldName when it returns a name', () => {\n const onSubFieldChange = jest.fn();\n const s = make({ onSubFieldChange, getFieldName: (handle) => 'named-' + handle.id });\n\n s.mathField = stubField({ r1: '1' });\n s.promptValues = {};\n\n s.onPromptInput();\n\n expect(onSubFieldChange).toHaveBeenCalledWith('named-r1', '1');\n });\n\n it('still tracks values when no callback is supplied', () => {\n const s = make();\n\n s.mathField = stubField({ r1: '3' });\n s.onPromptInput();\n\n expect(s.promptValues).toEqual({ r1: '3' });\n });\n });\n\n describe('onSubFieldFocus', () => {\n it('reports the prompt containing the selection, plus setInput', () => {\n const onSubFieldFocus = jest.fn();\n const setInput = jest.fn();\n const s = make({ onSubFieldFocus, setInput });\n\n // r2 occupies offsets 10..15\n s.mathField = stubField({ r1: '', r2: '' }, { ranges: [[11, 11]] });\n s.onPromptFocus();\n\n expect(onSubFieldFocus).toHaveBeenCalledTimes(1);\n expect(onSubFieldFocus.mock.calls[0][0]).toEqual('r2');\n expect(onSubFieldFocus.mock.calls[0][1].id).toEqual('r2');\n expect(setInput).toHaveBeenCalled();\n });\n\n it('does nothing without a selection', () => {\n const onSubFieldFocus = jest.fn();\n const s = make({ onSubFieldFocus });\n\n s.mathField = stubField({ r1: '' }, null);\n s.onPromptFocus();\n\n expect(onSubFieldFocus).not.toHaveBeenCalled();\n });\n\n it('does not throw when the field is missing', () => {\n const s = make({ onSubFieldFocus: jest.fn() });\n\n expect(() => s.onPromptFocus()).not.toThrow();\n });\n });\n\n describe('accessibility announcements', () => {\n afterEach(() => {\n document.body.innerHTML = '';\n });\n\n it('creates a polite off-screen live region', () => {\n const s = make();\n\n s.createLiveRegion();\n\n expect(s.liveRegion.getAttribute('aria-live')).toEqual('polite');\n expect(s.liveRegion.getAttribute('aria-atomic')).toEqual('true');\n expect(document.body.contains(s.liveRegion)).toBe(true);\n\n s.removeLiveRegion();\n expect(s.liveRegion).toBeUndefined();\n });\n\n it('does not create a second region', () => {\n const s = make();\n\n s.createLiveRegion();\n const first = s.liveRegion;\n\n s.createLiveRegion();\n expect(s.liveRegion).toBe(first);\n s.removeLiveRegion();\n });\n\n it('announces when typed input expands into a command', () => {\n const s = make();\n\n s.createLiveRegion();\n s.promptValues = { r1: 'x' };\n // grew by more than one char AND gained a backslash => a conversion\n s.announceConversion({ r1: 'x\\\\frac{}{}' });\n\n expect(s.liveRegion.textContent).toEqual('Converted to math symbol');\n s.removeLiveRegion();\n });\n\n it('does not announce ordinary typing', () => {\n const s = make();\n\n s.createLiveRegion();\n s.promptValues = { r1: 'x' };\n s.announceConversion({ r1: 'xy' });\n\n expect(s.liveRegion.textContent).toEqual('');\n s.removeLiveRegion();\n });\n\n it('does not announce a deletion', () => {\n const s = make();\n\n s.createLiveRegion();\n s.promptValues = { r1: 'x\\\\frac{}{}' };\n s.onKeyDown({ key: 'Backspace' });\n s.announceConversion({ r1: 'x' });\n\n expect(s.liveRegion.textContent).toEqual('');\n s.removeLiveRegion();\n });\n });\n\n describe('focus / blur', () => {\n it('delegates to the field and is safe without one', () => {\n const s = make();\n\n expect(() => {\n s.focus();\n s.blur();\n }).not.toThrow();\n\n s.mathField = stubField();\n s.focus();\n s.blur();\n\n expect(s.mathField.focused).toBe(true);\n expect(s.mathField.blurred).toBe(true);\n });\n });\n});\n"],"mappings":"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","ignoreList":[]}
|
package/lib/mathlive-instance.js
CHANGED
|
@@ -5,6 +5,7 @@ var _typeof = require("@babel/runtime/helpers/typeof");
|
|
|
5
5
|
Object.defineProperty(exports, "__esModule", {
|
|
6
6
|
value: true
|
|
7
7
|
});
|
|
8
|
+
exports.MATH_MODE_SPACE = void 0;
|
|
8
9
|
Object.defineProperty(exports, "NEWLINE_EMBED", {
|
|
9
10
|
enumerable: true,
|
|
10
11
|
get: function get() {
|
|
@@ -70,25 +71,61 @@ var absoluteFontsDirectory = function absoluteFontsDirectory(dir) {
|
|
|
70
71
|
}
|
|
71
72
|
};
|
|
72
73
|
|
|
74
|
+
/**
|
|
75
|
+
* Version-pinned CDN fallback for the KaTeX fonts.
|
|
76
|
+
*
|
|
77
|
+
* Some symbols are composed from Private Use Area glyphs that exist only in
|
|
78
|
+
* those fonts: `\\neq` is U+E020 (a negation slash) overlapped on `=`, and
|
|
79
|
+
* `\\nsim` / `\\ncong` work the same way. With no fonts the browser substitutes
|
|
80
|
+
* an arbitrary glyph for U+E020, so those keys render as garbage rather than
|
|
81
|
+
* simply falling back to a plainer face.
|
|
82
|
+
*
|
|
83
|
+
* The version comes from MathLive itself, so the fonts always match the engine.
|
|
84
|
+
* Hosts that would rather not depend on a third party should serve the fonts and
|
|
85
|
+
* call `configureFonts('/their/path')`, or pass `null` to opt out.
|
|
86
|
+
*/
|
|
87
|
+
var FALLBACK_MATHLIVE_VERSION = '0.110.0';
|
|
88
|
+
var cdnFontsDirectory = function cdnFontsDirectory() {
|
|
89
|
+
var version = mathlive && mathlive.version && mathlive.version.mathlive || FALLBACK_MATHLIVE_VERSION;
|
|
90
|
+
|
|
91
|
+
// NOTE: `/fonts`, not `/dist/fonts` - the published package root holds them.
|
|
92
|
+
return "https://unpkg.com/mathlive@".concat(version, "/fonts");
|
|
93
|
+
};
|
|
94
|
+
|
|
73
95
|
/**
|
|
74
96
|
* Tell the package where the MathLive fonts are served from.
|
|
75
97
|
*
|
|
76
|
-
* Call before `loadMathLive()`.
|
|
77
|
-
*
|
|
78
|
-
*
|
|
98
|
+
* Call before `loadMathLive()`.
|
|
99
|
+
*
|
|
100
|
+
* - a path/URL: served by you, no third-party request
|
|
101
|
+
* - `null`: skip font loading; note `\\neq`, `\\nsim` and `\\ncong` then
|
|
102
|
+
* render incorrectly, being built from Private Use Area glyphs
|
|
103
|
+
* - not called: falls back to a version-pinned CDN
|
|
79
104
|
*
|
|
80
105
|
* The fonts ship inside the `mathlive` package (`node_modules/mathlive/fonts`)
|
|
81
|
-
*
|
|
106
|
+
* ready to copy into whatever the host app serves statically.
|
|
82
107
|
*
|
|
83
|
-
* @param {string|null} dir
|
|
108
|
+
* @param {string|null} [dir]
|
|
84
109
|
*/
|
|
85
110
|
var configureFonts = exports.configureFonts = function configureFonts(dir) {
|
|
86
111
|
fontsDirectory = dir;
|
|
87
112
|
if (mathlive) {
|
|
88
|
-
mathlive.MathfieldElement.fontsDirectory = absoluteFontsDirectory(dir);
|
|
113
|
+
mathlive.MathfieldElement.fontsDirectory = dir === undefined ? cdnFontsDirectory() : absoluteFontsDirectory(dir);
|
|
89
114
|
}
|
|
90
115
|
};
|
|
91
116
|
|
|
117
|
+
/**
|
|
118
|
+
* What the spacebar inserts in math mode.
|
|
119
|
+
*
|
|
120
|
+
* MathLive's `mathModeSpace` defaults to an empty string, which makes the
|
|
121
|
+
* spacebar a no-op - users cannot type a space at all. `\\ ` (a LaTeX control
|
|
122
|
+
* space) is what MathQuill serialised spaces as, so existing content and the
|
|
123
|
+
* MathJax renderer in @pie-lib/math-rendering both already understand it.
|
|
124
|
+
*
|
|
125
|
+
* A literal " " is not usable: LaTeX skips raw whitespace.
|
|
126
|
+
*/
|
|
127
|
+
var MATH_MODE_SPACE = exports.MATH_MODE_SPACE = '\\ ';
|
|
128
|
+
|
|
92
129
|
/**
|
|
93
130
|
* Custom macros for the pie-specific commands that the MathQuill fork
|
|
94
131
|
* understood but stock LaTeX does not.
|
|
@@ -146,12 +183,12 @@ var loadWarned = false;
|
|
|
146
183
|
/** Apply post-load configuration exactly once. */
|
|
147
184
|
var configureLoaded = function configureLoaded() {
|
|
148
185
|
// Always replace MathLive's relative `'./fonts/'` default: it throws
|
|
149
|
-
// "Invalid base URL" when bundled.
|
|
150
|
-
//
|
|
186
|
+
// "Invalid base URL" when bundled. Use the host's copy if one was configured,
|
|
187
|
+
// otherwise the version-pinned CDN.
|
|
151
188
|
if (fontsDirectory === undefined) {
|
|
152
|
-
log('no fontsDirectory configured -
|
|
189
|
+
log('no fontsDirectory configured - falling back to the CDN (%s). ' + 'Serve the fonts yourself and call configureFonts("/path/to/fonts") to avoid the network request, ' + 'or configureFonts(null) to opt out.', cdnFontsDirectory());
|
|
153
190
|
}
|
|
154
|
-
mathlive.MathfieldElement.fontsDirectory = fontsDirectory === undefined ?
|
|
191
|
+
mathlive.MathfieldElement.fontsDirectory = fontsDirectory === undefined ? cdnFontsDirectory() : absoluteFontsDirectory(fontsDirectory);
|
|
155
192
|
injectCoreStylesheet();
|
|
156
193
|
};
|
|
157
194
|
|