Fix bug discovered by Fred where katex in dev mode would interfere with svelte figures updating

This commit is contained in:
Ludwig Schubert
2020-02-11 13:34:12 -08:00
parent 0d6de08c82
commit dccd48154b
8 changed files with 2022 additions and 1409 deletions
+39 -33
View File
@@ -45,38 +45,41 @@ import { DistillHeader } from './distill-components/distill-header';
import { DistillAppendix } from './distill-components/distill-appendix';
import { DistillFooter } from './distill-components/distill-footer';
const distillMain = function() {
if (window.distillRunlevel < 1) {
throw new Error('Insufficient Runlevel for Distill Template!');
let templateIsLoading = false;
let runlevel = 0;
const initialize = function() {
if (window.distill.runlevel < 1) {
throw new Error("Insufficient Runlevel for Distill Template!");
}
/* 1. Flag that we're being loaded */
if ('distillTemplateIsLoading' in window && window.distillTemplateIsLoading) {
throw new Error('Runlevel 1: Distill Template is getting loaded more than once, aborting!');
if ("distill" in window && window.distill.templateIsLoading) {
throw new Error(
"Runlevel 1: Distill Template is getting loaded more than once, aborting!"
);
} else {
window.distillTemplateIsLoading = true;
console.debug('Runlevel 1: Distill Template has started loading.');
window.distill.templateIsLoading = true;
console.debug("Runlevel 1: Distill Template has started loading.");
}
/* 2. Add styles if they weren't added during prerendering */
makeStyleTag(document);
console.debug('Runlevel 1: Static Distill styles have been added.');
console.debug('Runlevel 1->2.');
window.distillRunlevel += 1;
console.debug("Runlevel 1: Static Distill styles have been added.");
console.debug("Runlevel 1->2.");
window.distill.runlevel += 1;
/* 3. Register Controller listener functions */
/* Needs to happen before components to their connected callbacks have a controller to talk to. */
for (const [functionName, callback] of Object.entries(Controller.listeners)) {
if (typeof callback === 'function') {
if (typeof callback === "function") {
document.addEventListener(functionName, callback);
} else {
console.error('Runlevel 2: Controller listeners need to be functions!');
console.error("Runlevel 2: Controller listeners need to be functions!");
}
}
console.debug('Runlevel 2: We can now listen to controller events.');
console.debug('Runlevel 2->3.');
window.distillRunlevel += 1;
console.debug("Runlevel 2: We can now listen to controller events.");
console.debug("Runlevel 2->3.");
window.distill.runlevel += 1;
/* 4. Register components */
const components = [
@@ -85,22 +88,22 @@ const distillMain = function() {
Slider, Interstitial
];
const distillComponents = [
DistillHeader, DistillAppendix, DistillFooter,
];
const distillComponents = [DistillHeader, DistillAppendix, DistillFooter];
if (window.distillRunlevel < 2) {
throw new Error('Insufficient Runlevel for adding custom elements!');
if (window.distill.runlevel < 2) {
throw new Error("Insufficient Runlevel for adding custom elements!");
}
const allComponents = components.concat(distillComponents);
for (const component of allComponents) {
console.debug('Runlevel 2: Registering custom element: ' + component.is);
console.debug("Runlevel 2: Registering custom element: " + component.is);
customElements.define(component.is, component);
}
console.debug('Runlevel 3: Distill Template finished registering custom elements.');
console.debug('Runlevel 3->4.');
window.distillRunlevel += 1;
console.debug(
"Runlevel 3: Distill Template finished registering custom elements."
);
console.debug("Runlevel 3->4.");
window.distill.runlevel += 1;
// If template was added after DOMContentLoaded we may have missed that event.
// Controller will check for that case, so trigger the event explicitly:
@@ -108,17 +111,20 @@ const distillMain = function() {
Controller.listeners.DOMContentLoaded();
}
console.debug('Runlevel 4: Distill Template initialisation complete.');
console.debug("Runlevel 4: Distill Template initialisation complete.");
window.distill.templateIsLoading = false;
window.distill.templateHasLoaded = true;
};
window.distillRunlevel = 0;
window.distill = { runlevel, initialize, templateIsLoading };
/* 0. Check browser feature support; synchronously polyfill if needed */
if (Polyfills.browserSupportsAllFeatures()) {
console.debug('Runlevel 0: No need for polyfills.');
console.debug('Runlevel 0->1.');
window.distillRunlevel += 1;
distillMain();
console.debug("Runlevel 0: No need for polyfills.");
console.debug("Runlevel 0->1.");
window.distill.runlevel += 1;
window.distill.initialize();
} else {
console.debug('Runlevel 0: Distill Template is loading polyfills.');
Polyfills.load(distillMain);
console.debug("Runlevel 0: Distill Template is loading polyfills.");
Polyfills.load(window.distill.initialize);
}
+60 -42
View File
@@ -28,14 +28,16 @@ const findEndOfMath = function(delimiter, text, startIndex) {
while (index < text.length) {
const character = text[index];
if (braceLevel <= 0 &&
text.slice(index, index + delimLength) === delimiter) {
if (
braceLevel <= 0 &&
text.slice(index, index + delimLength) === delimiter
) {
return index;
} else if (character === '\\') {
} else if (character === "\\") {
index++;
} else if (character === '{') {
} else if (character === "{") {
braceLevel++;
} else if (character === '}') {
} else if (character === "}") {
braceLevel--;
}
@@ -49,7 +51,7 @@ const splitAtDelimiters = function(startData, leftDelim, rightDelim, display) {
const finalData = [];
for (let i = 0; i < startData.length; i++) {
if (startData[i].type === 'text') {
if (startData[i].type === "text") {
const text = startData[i].data;
let lookingForLeft = true;
@@ -60,13 +62,14 @@ const splitAtDelimiters = function(startData, leftDelim, rightDelim, display) {
if (nextIndex !== -1) {
currIndex = nextIndex;
finalData.push({
type: 'text',
data: text.slice(0, currIndex),
type: "text",
data: text.slice(0, currIndex)
});
lookingForLeft = false;
}
while (true) { // eslint-disable-line no-constant-condition
while (true) {
// eslint-disable-line no-constant-condition
if (lookingForLeft) {
nextIndex = text.indexOf(leftDelim, currIndex);
if (nextIndex === -1) {
@@ -74,8 +77,8 @@ const splitAtDelimiters = function(startData, leftDelim, rightDelim, display) {
}
finalData.push({
type: 'text',
data: text.slice(currIndex, nextIndex),
type: "text",
data: text.slice(currIndex, nextIndex)
});
currIndex = nextIndex;
@@ -83,20 +86,17 @@ const splitAtDelimiters = function(startData, leftDelim, rightDelim, display) {
nextIndex = findEndOfMath(
rightDelim,
text,
currIndex + leftDelim.length);
currIndex + leftDelim.length
);
if (nextIndex === -1) {
break;
}
finalData.push({
type: 'math',
data: text.slice(
currIndex + leftDelim.length,
nextIndex),
rawData: text.slice(
currIndex,
nextIndex + rightDelim.length),
display: display,
type: "math",
data: text.slice(currIndex + leftDelim.length, nextIndex),
rawData: text.slice(currIndex, nextIndex + rightDelim.length),
display: display
});
currIndex = nextIndex + rightDelim.length;
@@ -106,8 +106,8 @@ const splitAtDelimiters = function(startData, leftDelim, rightDelim, display) {
}
finalData.push({
type: 'text',
data: text.slice(currIndex),
type: "text",
data: text.slice(currIndex)
});
} else {
finalData.push(startData[i]);
@@ -117,14 +117,16 @@ const splitAtDelimiters = function(startData, leftDelim, rightDelim, display) {
return finalData;
};
const splitWithDelimiters = function(text, delimiters) {
let data = [{type: 'text', data: text}];
let data = [{ type: "text", data: text }];
for (let i = 0; i < delimiters.length; i++) {
const delimiter = delimiters[i];
data = splitAtDelimiters(
data, delimiter.left, delimiter.right,
delimiter.display || false);
data,
delimiter.left,
delimiter.right,
delimiter.display || false
);
}
return data;
};
@@ -137,10 +139,10 @@ const renderMathInText = function(text, optionsCopy) {
const fragment = document.createDocumentFragment();
for (let i = 0; i < data.length; i++) {
if (data[i].type === 'text') {
if (data[i].type === "text") {
fragment.appendChild(document.createTextNode(data[i].data));
} else {
const tag = document.createElement('d-math');
const tag = document.createElement("d-math");
const math = data[i].data;
// Override any display mode defined in the settings with that
// defined by the text itself
@@ -148,15 +150,14 @@ const renderMathInText = function(text, optionsCopy) {
try {
tag.textContent = math;
if (optionsCopy.displayMode) {
tag.setAttribute('block', '');
tag.setAttribute("block", "");
}
} catch (e) {
if (!(e instanceof katex.ParseError)) {
throw e;
}
optionsCopy.errorCallback(
'KaTeX auto-render: Failed to parse `' + data[i].data +
'` with ',
"KaTeX auto-render: Failed to parse `" + data[i].data + "` with ",
e
);
fragment.appendChild(document.createTextNode(data[i].rawData));
@@ -174,13 +175,17 @@ const renderElem = function(elem, optionsCopy) {
const childNode = elem.childNodes[i];
if (childNode.nodeType === 3) {
// Text node
const frag = renderMathInText(childNode.textContent, optionsCopy);
i += frag.childNodes.length - 1;
elem.replaceChild(frag, childNode);
const text = childNode.textContent;
if (optionsCopy.mightHaveMath(text)) {
const frag = renderMathInText(text, optionsCopy);
i += frag.childNodes.length - 1;
elem.replaceChild(frag, childNode);
}
} else if (childNode.nodeType === 1) {
// Element node
const shouldRender = optionsCopy.ignoredTags.indexOf(
childNode.nodeName.toLowerCase()) === -1;
const shouldRender =
optionsCopy.ignoredTags.indexOf(childNode.nodeName.toLowerCase()) ===
-1;
if (shouldRender) {
renderElem(childNode, optionsCopy);
@@ -192,27 +197,40 @@ const renderElem = function(elem, optionsCopy) {
const defaultAutoRenderOptions = {
delimiters: [
{left: '$$', right: '$$', display: true},
{left: '\\[', right: '\\]', display: true},
{left: '\\(', right: '\\)', display: false},
{ left: "$$", right: "$$", display: true },
{ left: "\\[", right: "\\]", display: true },
{ left: "\\(", right: "\\)", display: false }
// LaTeX uses this, but it ruins the display of normal `$` in text:
// {left: '$', right: '$', display: false},
],
ignoredTags: [
'script', 'noscript', 'style', 'textarea', 'pre', 'code', 'svg',
"script",
"noscript",
"style",
"textarea",
"pre",
"code",
"svg"
],
errorCallback: function(msg, err) {
console.error(msg, err);
},
}
};
export const renderMathInElement = function(elem, options) {
if (!elem) {
throw new Error('No element provided to render');
throw new Error("No element provided to render");
}
const optionsCopy = Object.assign({}, defaultAutoRenderOptions, options);
const delimiterStrings = optionsCopy.delimiters.flatMap(d => [
d.left,
d.right
]);
const mightHaveMath = text =>
delimiterStrings.some(d => text.indexOf(d) !== -1);
optionsCopy.mightHaveMath = mightHaveMath;
renderElem(elem, optionsCopy);
};