[IMPROVE] Lazyload Katex Package (#15398)

pull/15599/head
Guilherme Gazzo 6 years ago committed by GitHub
parent a6080f7e31
commit 40eb927e4e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
  1. 207
      app/katex/client/index.js
  2. 194
      app/katex/lib/katex.js
  3. 2
      client/importsCss.js

@ -1 +1,206 @@
export { default as katex } from '../lib/katex';
import { Random } from 'meteor/random';
import { Tracker } from 'meteor/tracker';
import _ from 'underscore';
import s from 'underscore.string';
import { callbacks } from '../../callbacks';
import { settings } from '../../settings';
class Boundary {
length() {
return this.end - this.start;
}
extract(str) {
return str.substr(this.start, this.length());
}
}
class Katex {
constructor(katex) {
this.katex = katex;
this.delimitersMap = [
{
opener: '\\[',
closer: '\\]',
displayMode: true,
enabled: () => this.isParenthesisSyntaxEnabled(),
}, {
opener: '\\(',
closer: '\\)',
displayMode: false,
enabled: () => this.isParenthesisSyntaxEnabled(),
}, {
opener: '$$',
closer: '$$',
displayMode: true,
enabled: () => this.isDollarSyntaxEnabled(),
}, {
opener: '$',
closer: '$',
displayMode: false,
enabled: () => this.isDollarSyntaxEnabled(),
},
];
}
findOpeningDelimiter(str, start) {
const matches = this.delimitersMap.filter((options) => options.enabled()).map((options) => ({
options,
pos: str.indexOf(options.opener, start),
}));
const positions = matches.filter(({ pos }) => pos >= 0).map(({ pos }) => pos);
// No opening delimiters were found
if (positions.length === 0) {
return null;
}
// Take the first delimiter found
const minPos = Math.min.apply(Math, positions);
const matchIndex = matches.findIndex(({ pos }) => pos === minPos);
const match = matches[matchIndex];
return match;
}
getLatexBoundaries(str, { options: { closer }, pos }) {
const closerIndex = str.substr(pos + closer.length).indexOf(closer);
if (closerIndex < 0) {
return null;
}
const inner = new Boundary();
const outer = new Boundary();
inner.start = pos + closer.length;
inner.end = inner.start + closerIndex;
outer.start = pos;
outer.end = inner.end + closer.length;
return {
outer,
inner,
};
}
// Searches for the first latex block in the given string
findLatex(str) {
let start = 0;
let openingDelimiterMatch;
while ((openingDelimiterMatch = this.findOpeningDelimiter(str, start++)) != null) {
const match = this.getLatexBoundaries(str, openingDelimiterMatch);
if (match && match.inner.extract(str).trim().length) {
match.options = openingDelimiterMatch.options;
return match;
}
}
return null;
}
// Breaks a message to what comes before, after and to the content of a
// matched latex block
extractLatex(str, match) {
const before = str.substr(0, match.outer.start);
const after = str.substr(match.outer.end);
let latex = match.inner.extract(str);
latex = s.unescapeHTML(latex);
return {
before,
latex,
after,
};
}
// Takes a latex math string and the desired display mode and renders it
// to HTML using the KaTeX library
renderLatex = (latex, displayMode) => {
try {
return this.katex.renderToString(latex, {
displayMode,
macros: {
'\\href': '\\@secondoftwo',
},
});
} catch ({ message }) {
return `<div class="katex-error katex-${ displayMode ? 'block' : 'inline' }-error">`
+ `${ s.escapeHTML(message) }</div>`;
}
}
// Takes a string and renders all latex blocks inside it
render(str, renderFunction) {
let result = '';
while (this.findLatex(str) != null) {
// Find the first latex block in the string
const match = this.findLatex(str);
const parts = this.extractLatex(str, match);
// Add to the reuslt what comes before the latex block as well as
// the rendered latex content
const rendered = renderFunction(parts.latex, match.options.displayMode);
result += parts.before + rendered;
// Set what comes after the latex block to be examined next
str = parts.after;
}
result += str;
return result;
}
renderMessage = (message) => {
if (_.isString(message)) {
return this.render(message, this.renderLatex);
}
if (!s.trim(message.html)) {
return message;
}
if (message.tokens == null) {
message.tokens = [];
}
message.html = this.render(message.html, (latex, displayMode) => {
const token = `=!=${ Random.id() }=!=`;
message.tokens.push({
token,
text: this.renderLatex(latex, displayMode),
});
return token;
});
return message;
}
isEnabled = () => settings.get('Katex_Enabled')
isDollarSyntaxEnabled = () => settings.get('Katex_Dollar_Syntax')
isParenthesisSyntaxEnabled = () => settings.get('Katex_Parenthesis_Syntax')
}
Tracker.autorun(async () => {
if (!settings.get('Katex_Enabled')) {
callbacks.remove('renderMessage', 'katex');
}
const [katex] = await Promise.all([import('katex'), import('./style.css'), import('../katex.min.css')]);
const instance = new Katex(katex);
callbacks.add('renderMessage', instance.renderMessage, callbacks.priority.HIGH - 1, 'katex');
});
export default {
isEnabled: () => settings.get('Katex_Enabled'),
isDollarSyntaxEnabled: () => settings.get('Katex_Dollar_Syntax'),
isParenthesisSyntaxEnabled: () => settings.get('Katex_Parenthesis_Syntax'),
};

@ -1,194 +1,10 @@
import { Random } from 'meteor/random';
import _ from 'underscore';
import s from 'underscore.string';
import katex from 'katex';
import { callbacks } from '../../callbacks';
import { settings } from '../../settings';
class Boundary {
length() {
return this.end - this.start;
}
extract(str) {
return str.substr(this.start, this.length());
}
}
class Katex {
constructor() {
this.delimitersMap = [
{
opener: '\\[',
closer: '\\]',
displayMode: true,
enabled: () => this.isParenthesisSyntaxEnabled(),
}, {
opener: '\\(',
closer: '\\)',
displayMode: false,
enabled: () => this.isParenthesisSyntaxEnabled(),
}, {
opener: '$$',
closer: '$$',
displayMode: true,
enabled: () => this.isDollarSyntaxEnabled(),
}, {
opener: '$',
closer: '$',
displayMode: false,
enabled: () => this.isDollarSyntaxEnabled(),
},
];
}
findOpeningDelimiter(str, start) {
const matches = this.delimitersMap.filter((options) => options.enabled()).map((options) => ({
options,
pos: str.indexOf(options.opener, start),
}));
const positions = matches.filter(({ pos }) => pos >= 0).map(({ pos }) => pos);
// No opening delimiters were found
if (positions.length === 0) {
return null;
}
// Take the first delimiter found
const minPos = Math.min.apply(Math, positions);
const matchIndex = matches.findIndex(({ pos }) => pos === minPos);
const match = matches[matchIndex];
return match;
}
getLatexBoundaries(str, { options: { closer }, pos }) {
const closerIndex = str.substr(pos + closer.length).indexOf(closer);
if (closerIndex < 0) {
return null;
}
const inner = new Boundary();
const outer = new Boundary();
inner.start = pos + closer.length;
inner.end = inner.start + closerIndex;
outer.start = pos;
outer.end = inner.end + closer.length;
return {
outer,
inner,
};
}
// Searches for the first latex block in the given string
findLatex(str) {
let start = 0;
let openingDelimiterMatch;
while ((openingDelimiterMatch = this.findOpeningDelimiter(str, start++)) != null) {
const match = this.getLatexBoundaries(str, openingDelimiterMatch);
if (match && match.inner.extract(str).trim().length) {
match.options = openingDelimiterMatch.options;
return match;
}
}
return null;
}
// Breaks a message to what comes before, after and to the content of a
// matched latex block
extractLatex(str, match) {
const before = str.substr(0, match.outer.start);
const after = str.substr(match.outer.end);
let latex = match.inner.extract(str);
latex = s.unescapeHTML(latex);
return {
before,
latex,
after,
};
}
// Takes a latex math string and the desired display mode and renders it
// to HTML using the KaTeX library
renderLatex = (latex, displayMode) => {
try {
return katex.renderToString(latex, {
displayMode,
macros: {
'\\href': '\\@secondoftwo',
},
});
} catch ({ message }) {
return `<div class="katex-error katex-${ displayMode ? 'block' : 'inline' }-error">`
+ `${ s.escapeHTML(message) }</div>`;
}
}
// Takes a string and renders all latex blocks inside it
render(str, renderFunction) {
let result = '';
while (this.findLatex(str) != null) {
// Find the first latex block in the string
const match = this.findLatex(str);
const parts = this.extractLatex(str, match);
// Add to the reuslt what comes before the latex block as well as
// the rendered latex content
const rendered = renderFunction(parts.latex, match.options.displayMode);
result += parts.before + rendered;
// Set what comes after the latex block to be examined next
str = parts.after;
}
result += str;
return result;
}
renderMessage = (message) => {
if (!this.isEnabled()) {
return message;
}
if (_.isString(message)) {
return this.render(message, this.renderLatex);
}
if (!s.trim(message.html)) {
return message;
}
if (message.tokens == null) {
message.tokens = [];
}
message.html = this.render(message.html, (latex, displayMode) => {
const token = `=!=${ Random.id() }=!=`;
message.tokens.push({
token,
text: this.renderLatex(latex, displayMode),
});
return token;
});
return message;
}
isEnabled = () => settings.get('Katex_Enabled')
isDollarSyntaxEnabled = () => settings.get('Katex_Dollar_Syntax')
isParenthesisSyntaxEnabled = () => settings.get('Katex_Parenthesis_Syntax')
}
const instance = new Katex();
export default {
isEnabled: () => settings.get('Katex_Enabled'),
callbacks.add('renderMessage', instance.renderMessage, callbacks.priority.HIGH - 1, 'katex');
isDollarSyntaxEnabled: () => settings.get('Katex_Dollar_Syntax'),
export default instance;
isParenthesisSyntaxEnabled: () => settings.get('Katex_Parenthesis_Syntax'),
};

@ -17,7 +17,6 @@ import '../app/emoji-emojione/client/emojione-sprites.css';
import '../app/github-enterprise/client/github-enterprise-login-button.css';
import '../app/gitlab/client/gitlab-login-button.css';
import '../app/integrations/client/stylesheets/integrations.css';
import '../app/katex/client/style.css';
import '../app/livechat/client/stylesheets/livechat.less';
import '../app/livestream/client/styles/liveStreamTab.css';
import '../app/message-action/client/stylesheets/messageAction.css';
@ -37,4 +36,3 @@ import '../app/ui-clean-history/client/views/stylesheets/cleanHistory.css';
import '../app/ui-vrecord/client/vrecord.css';
import '../app/videobridge/client/stylesheets/video.less';
import '../app/wordpress/client/wordpress-login-button.css';
import '../app/katex/katex.min.css';

Loading…
Cancel
Save