Commit 4d1f5837 authored by Sean McGivern's avatar Sean McGivern

Merge branch 'katex-math' into 'master'

Render math in Asciidoc and Markdown with KaTeX using code blocks

Closes #13690 and #13180

See merge request !8003
parents ecfa8655 2d170a20
...@@ -60,7 +60,7 @@ ...@@ -60,7 +60,7 @@
content: this.editor.getValue() content: this.editor.getValue()
}, function(response) { }, function(response) {
currentPane.empty().append(response); currentPane.empty().append(response);
return currentPane.syntaxHighlight(); return currentPane.renderGFM();
}); });
} else { } else {
this.$toggleButton.show(); this.$toggleButton.show();
......
...@@ -309,7 +309,7 @@ ...@@ -309,7 +309,7 @@
} }
row = form.closest("tr"); row = form.closest("tr");
note_html = $(note.html); note_html = $(note.html);
note_html.syntaxHighlight(); note_html.renderGFM();
// is this the first note of discussion? // is this the first note of discussion?
discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']"); discussionContainer = $(".notes[data-discussion-id='" + note.discussion_id + "']");
if ((note.original_discussion_id != null) && discussionContainer.length === 0) { if ((note.original_discussion_id != null) && discussionContainer.length === 0) {
...@@ -326,7 +326,7 @@ ...@@ -326,7 +326,7 @@
discussionContainer.append(note_html); discussionContainer.append(note_html);
// Init discussion on 'Discussion' page if it is merge request page // Init discussion on 'Discussion' page if it is merge request page
if ($('body').attr('data-page').indexOf('projects:merge_request') === 0) { if ($('body').attr('data-page').indexOf('projects:merge_request') === 0) {
$('ul.main-notes-list').append(note.discussion_html).syntaxHighlight(); $('ul.main-notes-list').append(note.discussion_html).renderGFM();
} }
} else { } else {
// append new note to all matching discussions // append new note to all matching discussions
...@@ -467,7 +467,7 @@ ...@@ -467,7 +467,7 @@
// Convert returned HTML to a jQuery object so we can modify it further // Convert returned HTML to a jQuery object so we can modify it further
$html = $(note.html); $html = $(note.html);
gl.utils.localTimeAgo($('.js-timeago', $html)); gl.utils.localTimeAgo($('.js-timeago', $html));
$html.syntaxHighlight(); $html.renderGFM();
$html.find('.js-task-list-container').taskList('enable'); $html.find('.js-task-list-container').taskList('enable');
// Find the note's `li` element by ID and replace it with the updated HTML // Find the note's `li` element by ID and replace it with the updated HTML
$note_li = $('.note-row-' + note.id); $note_li = $('.note-row-' + note.id);
......
...@@ -28,7 +28,7 @@ ...@@ -28,7 +28,7 @@
return this.renderMarkdown(mdText, (function(_this) { return this.renderMarkdown(mdText, (function(_this) {
return function(response) { return function(response) {
preview.html(response.body); preview.html(response.body);
preview.syntaxHighlight(); preview.renderGFM();
return _this.renderReferencedUsers(response.references.users, form); return _this.renderReferencedUsers(response.references.users, form);
}; };
})(this)); })(this));
......
/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, no-undef, no-else-return, prefer-arrow-callback, padded-blocks, max-len */
// Render Gitlab flavoured Markdown
//
// Delegates to syntax highlight and render math
//
(function() {
$.fn.renderGFM = function() {
this.find('.js-syntax-highlight').syntaxHighlight();
this.find('.js-render-math').renderMath();
};
$(document).on('ready page:load', function() {
return $('body').renderGFM();
});
}).call(this);
/* eslint-disable func-names, space-before-function-paren, consistent-return, no-var, no-undef, no-else-return, prefer-arrow-callback, padded-blocks, max-len */
// Renders math using KaTeX in any element with the
// `js-render-math` class
//
// ### Example Markup
//
// <code class="js-render-math"></div>
//
(function() {
// Only load once
var katexLoaded = false;
// Loop over all math elements and render math
var renderWithKaTeX = function (elements) {
elements.each(function () {
var mathNode = $('<span></span>');
var $this = $(this);
var display = $this.attr('data-math-style') === 'display';
try {
katex.render($this.text(), mathNode.get(0), { displayMode: display });
mathNode.insertAfter($this);
$this.remove();
} catch (err) {
// What can we do??
console.log(err.message);
}
});
};
$.fn.renderMath = function() {
var $this = this;
if ($this.length === 0) return;
if (katexLoaded) renderWithKaTeX($this);
else {
// Request CSS file so it is in the cache
$.get(gon.katex_css_url, function() {
var css = $('<link>',
{ rel: 'stylesheet',
type: 'text/css',
href: gon.katex_css_url,
});
css.appendTo('head');
// Load KaTeX js
$.getScript(gon.katex_js_url, function() {
katexLoaded = true;
renderWithKaTeX($this); // Run KaTeX
});
});
}
};
}).call(this);
...@@ -10,8 +10,10 @@ ...@@ -10,8 +10,10 @@
// <div class="js-syntax-highlight"></div> // <div class="js-syntax-highlight"></div>
// //
(function() { (function() {
$.fn.syntaxHighlight = function() { $.fn.syntaxHighlight = function() {
var $children; var $children;
if ($(this).hasClass('js-syntax-highlight')) { if ($(this).hasClass('js-syntax-highlight')) {
// Given the element itself, apply highlighting // Given the element itself, apply highlighting
return $(this).addClass(gon.user_color_scheme); return $(this).addClass(gon.user_color_scheme);
...@@ -24,8 +26,4 @@ ...@@ -24,8 +26,4 @@
} }
}; };
$(document).on('ready page:load', function() {
return $('.js-syntax-highlight').syntaxHighlight();
});
}).call(this); }).call(this);
---
title: Added support for math rendering, using KaTeX, in Markdown and asciidoc
merge_request: 8003
author: Munken
...@@ -85,6 +85,8 @@ module Gitlab ...@@ -85,6 +85,8 @@ module Gitlab
config.assets.precompile << "print.css" config.assets.precompile << "print.css"
config.assets.precompile << "notify.css" config.assets.precompile << "notify.css"
config.assets.precompile << "mailers/*.css" config.assets.precompile << "mailers/*.css"
config.assets.precompile << "katex.css"
config.assets.precompile << "katex.js"
config.assets.precompile << "graphs/graphs_bundle.js" config.assets.precompile << "graphs/graphs_bundle.js"
config.assets.precompile << "users/users_bundle.js" config.assets.precompile << "users/users_bundle.js"
config.assets.precompile << "network/network_bundle.js" config.assets.precompile << "network/network_bundle.js"
......
# Touch the lexers so it is registered with Rouge
Rouge::Lexers::Math
...@@ -319,6 +319,40 @@ Here's a sample video: ...@@ -319,6 +319,40 @@ Here's a sample video:
![Sample Video](img/markdown_video.mp4) ![Sample Video](img/markdown_video.mp4)
### Math
> If this is not rendered correctly, see
https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md#math
It is possible to have math written with the LaTeX syntax rendered using [KaTeX][katex].
Math written inside ```$``$``` will be rendered inline with the text.
Math written inside triple back quotes, with the language declared as `math`, will be rendered on a separate line.
Example:
This math is inline $`a^2+b^2=c^2`$.
This is on a separate line
```math
a^2+b^2=c^2
```
Becomes:
This math is inline $`a^2+b^2=c^2`$.
This is on a separate line
```math
a^2+b^2=c^2
```
_Be advised that KaTeX only supports a [subset][katex-subset] of LaTeX._
>**Note:**
This also works for the asciidoctor `:stem: latexmath`. For details see the [asciidoctor user manual][asciidoctor-manual].
## Standard Markdown ## Standard Markdown
### Headers ### Headers
...@@ -764,3 +798,6 @@ A link starting with a `/` is relative to the wiki root. ...@@ -764,3 +798,6 @@ A link starting with a `/` is relative to the wiki root.
[markdown.md]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md [markdown.md]: https://gitlab.com/gitlab-org/gitlab-ce/blob/master/doc/user/markdown.md
[rouge]: http://rouge.jneen.net/ "Rouge website" [rouge]: http://rouge.jneen.net/ "Rouge website"
[redcarpet]: https://github.com/vmg/redcarpet "Redcarpet website" [redcarpet]: https://github.com/vmg/redcarpet "Redcarpet website"
[katex]: https://github.com/Khan/KaTeX "KaTeX website"
[katex-subset]: https://github.com/Khan/KaTeX/wiki/Function-Support-in-KaTeX "Macros supported by KaTeX"
[asciidoctor-manual]: http://asciidoctor.org/docs/user-manual/#activating-stem-support "Asciidoctor user manual"
\ No newline at end of file
require 'uri'
module Banzai
module Filter
# HTML filter that adds class="code math" and removes the dollar sign in $`2+2`$.
#
class MathFilter < HTML::Pipeline::Filter
# This picks out <code>...</code>.
INLINE_MATH = 'descendant-or-self::code'.freeze
# Pick out a code block which is declared math
DISPLAY_MATH = "descendant-or-self::pre[contains(@class, 'math') and contains(@class, 'code')]".freeze
# Attribute indicating inline or display math.
STYLE_ATTRIBUTE = 'data-math-style'.freeze
# Class used for tagging elements that should be rendered
TAG_CLASS = 'js-render-math'.freeze
INLINE_CLASSES = "code math #{TAG_CLASS}".freeze
DOLLAR_SIGN = '$'.freeze
def call
doc.xpath(INLINE_MATH).each do |code|
closing = code.next
opening = code.previous
# We need a sibling before and after.
# They should end and start with $ respectively.
if closing && opening &&
closing.content.first == DOLLAR_SIGN &&
opening.content.last == DOLLAR_SIGN
code[:class] = INLINE_CLASSES
code[STYLE_ATTRIBUTE] = 'inline'
closing.content = closing.content[1..-1]
opening.content = opening.content[0..-2]
end
end
doc.xpath(DISPLAY_MATH).each do |el|
el[STYLE_ATTRIBUTE] = 'display'
el[:class] += " #{TAG_CLASS}"
end
doc
end
end
end
end
...@@ -6,6 +6,7 @@ module Banzai ...@@ -6,6 +6,7 @@ module Banzai
Filter::SyntaxHighlightFilter, Filter::SyntaxHighlightFilter,
Filter::SanitizationFilter, Filter::SanitizationFilter,
Filter::MathFilter,
Filter::UploadLinkFilter, Filter::UploadLinkFilter,
Filter::VideoLinkFilter, Filter::VideoLinkFilter,
Filter::ImageLinkFilter, Filter::ImageLinkFilter,
......
require 'asciidoctor' require 'asciidoctor'
require 'asciidoctor/converter/html5'
module Gitlab module Gitlab
# Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters # Parser/renderer for the AsciiDoc format that uses Asciidoctor and filters
...@@ -23,7 +24,7 @@ module Gitlab ...@@ -23,7 +24,7 @@ module Gitlab
def self.render(input, context, asciidoc_opts = {}) def self.render(input, context, asciidoc_opts = {})
asciidoc_opts.reverse_merge!( asciidoc_opts.reverse_merge!(
safe: :secure, safe: :secure,
backend: :html5, backend: :gitlab_html5,
attributes: [] attributes: []
) )
asciidoc_opts[:attributes].unshift(*DEFAULT_ADOC_ATTRS) asciidoc_opts[:attributes].unshift(*DEFAULT_ADOC_ATTRS)
...@@ -36,3 +37,31 @@ module Gitlab ...@@ -36,3 +37,31 @@ module Gitlab
end end
end end
end end
module Gitlab
module Asciidoc
class Html5Converter < Asciidoctor::Converter::Html5Converter
extend Asciidoctor::Converter::Config
register_for 'gitlab_html5'
def stem(node)
return super unless node.style.to_sym == :latexmath
%(<pre#{id_attribute(node)} class="code math js-render-math #{node.role}" data-math-style="display"><code>#{node.content}</code></pre>)
end
def inline_quoted(node)
return super unless node.type.to_sym == :latexmath
%(<code#{id_attribute(node)} class="code math js-render-math #{node.role}" data-math-style="inline">#{node.text}</code>)
end
private
def id_attribute(node)
node.id ? %( id="#{node.id}") : nil
end
end
end
end
...@@ -8,6 +8,8 @@ module Gitlab ...@@ -8,6 +8,8 @@ module Gitlab
gon.shortcuts_path = help_page_path('shortcuts') gon.shortcuts_path = help_page_path('shortcuts')
gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class gon.user_color_scheme = Gitlab::ColorSchemes.for_user(current_user).css_class
gon.award_menu_url = emojis_path gon.award_menu_url = emojis_path
gon.katex_css_url = ActionController::Base.helpers.asset_path('katex.css')
gon.katex_js_url = ActionController::Base.helpers.asset_path('katex.js')
if current_user if current_user
gon.current_user_id = current_user.id gon.current_user_id = current_user.id
......
module Rouge
module Lexers
class Math < Lexer
title "A passthrough lexer used for LaTeX input"
desc "A boring lexer that doesn't highlight anything"
tag 'math'
mimetypes 'text/plain'
default_options token: 'Text'
def token
@token ||= Token[option :token]
end
def stream_tokens(string, &b)
yield self.token, string
end
end
end
end
require 'spec_helper'
describe Banzai::Filter::MathFilter, lib: true do
include FilterSpecHelper
it 'leaves regular inline code unchanged' do
input = "<code>2+2</code>"
doc = filter(input)
expect(doc.to_s).to eq input
end
it 'removes surrounding dollar signs and adds class code, math and js-render-math' do
doc = filter("$<code>2+2</code>$")
expect(doc.to_s).to eq '<code class="code math js-render-math" data-math-style="inline">2+2</code>'
end
it 'only removes surrounding dollar signs' do
doc = filter("test $<code>2+2</code>$ test")
before = doc.xpath('descendant-or-self::text()[1]').first
after = doc.xpath('descendant-or-self::text()[3]').first
expect(before.to_s).to eq 'test '
expect(after.to_s).to eq ' test'
end
it 'only removes surrounding single dollar sign' do
doc = filter("test $$<code>2+2</code>$$ test")
before = doc.xpath('descendant-or-self::text()[1]').first
after = doc.xpath('descendant-or-self::text()[3]').first
expect(before.to_s).to eq 'test $'
expect(after.to_s).to eq '$ test'
end
it 'adds data-math-style inline attribute to inline math' do
doc = filter('$<code>2+2</code>$')
code = doc.xpath('descendant-or-self::code').first
expect(code['data-math-style']).to eq 'inline'
end
it 'adds class code and math to inline math' do
doc = filter('$<code>2+2</code>$')
code = doc.xpath('descendant-or-self::code').first
expect(code[:class]).to include("code")
expect(code[:class]).to include("math")
end
it 'adds js-render-math class to inline math' do
doc = filter('$<code>2+2</code>$')
code = doc.xpath('descendant-or-self::code').first
expect(code[:class]).to include("js-render-math")
end
# Cases with faulty syntax. Should be a no-op
it 'ignores cases with missing dolar sign at the end' do
input = "test $<code>2+2</code> test"
doc = filter(input)
expect(doc.to_s).to eq input
end
it 'ignores cases with missing dolar sign at the beginning' do
input = "test <code>2+2</code>$ test"
doc = filter(input)
expect(doc.to_s).to eq input
end
it 'ignores dollar signs if it is not adjacent' do
input = '<p>We check strictly $<code>2+2</code> and <code>2+2</code>$ </p>'
doc = filter(input)
expect(doc.to_s).to eq input
end
# Display math
it 'adds data-math-style display attribute to display math' do
doc = filter('<pre class="code highlight js-syntax-highlight math" v-pre="true"><code>2+2</code></pre>')
pre = doc.xpath('descendant-or-self::pre').first
expect(pre['data-math-style']).to eq 'display'
end
it 'adds js-render-math class to display math' do
doc = filter('<pre class="code highlight js-syntax-highlight math" v-pre="true"><code>2+2</code></pre>')
pre = doc.xpath('descendant-or-self::pre').first
expect(pre[:class]).to include("js-render-math")
end
it 'ignores code blocks that are not math' do
input = '<pre class="code highlight js-syntax-highlight plaintext" v-pre="true"><code>2+2</code></pre>'
doc = filter(input)
expect(doc.to_s).to eq input
end
it 'requires the pre to contain both code and math' do
input = '<pre class="highlight js-syntax-highlight plaintext math" v-pre="true"><code>2+2</code></pre>'
doc = filter(input)
expect(doc.to_s).to eq input
end
it 'dollar signs around to display math' do
doc = filter('$<pre class="code highlight js-syntax-highlight math" v-pre="true"><code>2+2</code></pre>$')
before = doc.xpath('descendant-or-self::text()[1]').first
after = doc.xpath('descendant-or-self::text()[3]').first
expect(before.to_s).to eq '$'
expect(after.to_s).to eq '$'
end
end
...@@ -11,7 +11,7 @@ module Gitlab ...@@ -11,7 +11,7 @@ module Gitlab
it "converts the input using Asciidoctor and default options" do it "converts the input using Asciidoctor and default options" do
expected_asciidoc_opts = { expected_asciidoc_opts = {
safe: :secure, safe: :secure,
backend: :html5, backend: :gitlab_html5,
attributes: described_class::DEFAULT_ADOC_ATTRS attributes: described_class::DEFAULT_ADOC_ATTRS
} }
...@@ -27,7 +27,7 @@ module Gitlab ...@@ -27,7 +27,7 @@ module Gitlab
it "merges the options with default ones" do it "merges the options with default ones" do
expected_asciidoc_opts = { expected_asciidoc_opts = {
safe: :safe, safe: :safe,
backend: :html5, backend: :gitlab_html5,
attributes: described_class::DEFAULT_ADOC_ATTRS + ['foo'] attributes: described_class::DEFAULT_ADOC_ATTRS + ['foo']
} }
......
This source diff could not be displayed because it is too large. You can view the blob instead.
@font-face {
font-family: 'KaTeX_AMS';
src: url('KaTeX_AMS-Regular.eot');
src: url('KaTeX_AMS-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_AMS-Regular.woff2') format('woff2'), url('KaTeX_AMS-Regular.woff') format('woff'), url('KaTeX_AMS-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Caligraphic';
src: url('KaTeX_Caligraphic-Bold.eot');
src: url('KaTeX_Caligraphic-Bold.eot#iefix') format('embedded-opentype'), url('KaTeX_Caligraphic-Bold.woff2') format('woff2'), url('KaTeX_Caligraphic-Bold.woff') format('woff'), url('KaTeX_Caligraphic-Bold.ttf') format('truetype');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Caligraphic';
src: url('KaTeX_Caligraphic-Regular.eot');
src: url('KaTeX_Caligraphic-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Caligraphic-Regular.woff2') format('woff2'), url('KaTeX_Caligraphic-Regular.woff') format('woff'), url('KaTeX_Caligraphic-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Fraktur';
src: url('KaTeX_Fraktur-Bold.eot');
src: url('KaTeX_Fraktur-Bold.eot#iefix') format('embedded-opentype'), url('KaTeX_Fraktur-Bold.woff2') format('woff2'), url('KaTeX_Fraktur-Bold.woff') format('woff'), url('KaTeX_Fraktur-Bold.ttf') format('truetype');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Fraktur';
src: url('KaTeX_Fraktur-Regular.eot');
src: url('KaTeX_Fraktur-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Fraktur-Regular.woff2') format('woff2'), url('KaTeX_Fraktur-Regular.woff') format('woff'), url('KaTeX_Fraktur-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Main';
src: url('KaTeX_Main-Bold.eot');
src: url('KaTeX_Main-Bold.eot#iefix') format('embedded-opentype'), url('KaTeX_Main-Bold.woff2') format('woff2'), url('KaTeX_Main-Bold.woff') format('woff'), url('KaTeX_Main-Bold.ttf') format('truetype');
font-weight: bold;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Main';
src: url('KaTeX_Main-Italic.eot');
src: url('KaTeX_Main-Italic.eot#iefix') format('embedded-opentype'), url('KaTeX_Main-Italic.woff2') format('woff2'), url('KaTeX_Main-Italic.woff') format('woff'), url('KaTeX_Main-Italic.ttf') format('truetype');
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: 'KaTeX_Main';
src: url('KaTeX_Main-Regular.eot');
src: url('KaTeX_Main-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Main-Regular.woff2') format('woff2'), url('KaTeX_Main-Regular.woff') format('woff'), url('KaTeX_Main-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Math';
src: url('KaTeX_Math-Italic.eot');
src: url('KaTeX_Math-Italic.eot#iefix') format('embedded-opentype'), url('KaTeX_Math-Italic.woff2') format('woff2'), url('KaTeX_Math-Italic.woff') format('woff'), url('KaTeX_Math-Italic.ttf') format('truetype');
font-weight: normal;
font-style: italic;
}
@font-face {
font-family: 'KaTeX_SansSerif';
src: url('KaTeX_SansSerif-Regular.eot');
src: url('KaTeX_SansSerif-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_SansSerif-Regular.woff2') format('woff2'), url('KaTeX_SansSerif-Regular.woff') format('woff'), url('KaTeX_SansSerif-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Script';
src: url('KaTeX_Script-Regular.eot');
src: url('KaTeX_Script-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Script-Regular.woff2') format('woff2'), url('KaTeX_Script-Regular.woff') format('woff'), url('KaTeX_Script-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Size1';
src: url('KaTeX_Size1-Regular.eot');
src: url('KaTeX_Size1-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Size1-Regular.woff2') format('woff2'), url('KaTeX_Size1-Regular.woff') format('woff'), url('KaTeX_Size1-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Size2';
src: url('KaTeX_Size2-Regular.eot');
src: url('KaTeX_Size2-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Size2-Regular.woff2') format('woff2'), url('KaTeX_Size2-Regular.woff') format('woff'), url('KaTeX_Size2-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Size3';
src: url('KaTeX_Size3-Regular.eot');
src: url('KaTeX_Size3-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Size3-Regular.woff2') format('woff2'), url('KaTeX_Size3-Regular.woff') format('woff'), url('KaTeX_Size3-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Size4';
src: url('KaTeX_Size4-Regular.eot');
src: url('KaTeX_Size4-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Size4-Regular.woff2') format('woff2'), url('KaTeX_Size4-Regular.woff') format('woff'), url('KaTeX_Size4-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
@font-face {
font-family: 'KaTeX_Typewriter';
src: url('KaTeX_Typewriter-Regular.eot');
src: url('KaTeX_Typewriter-Regular.eot#iefix') format('embedded-opentype'), url('KaTeX_Typewriter-Regular.woff2') format('woff2'), url('KaTeX_Typewriter-Regular.woff') format('woff'), url('KaTeX_Typewriter-Regular.ttf') format('truetype');
font-weight: normal;
font-style: normal;
}
.katex-display {
display: block;
margin: 1em 0;
text-align: center;
}
.katex-display > .katex {
display: inline-block;
text-align: initial;
}
.katex {
font: normal 1.21em KaTeX_Main, Times New Roman, serif;
line-height: 1.2;
white-space: nowrap;
text-indent: 0;
}
.katex .katex-html {
display: inline-block;
}
.katex .katex-mathml {
position: absolute;
clip: rect(1px, 1px, 1px, 1px);
padding: 0;
border: 0;
height: 1px;
width: 1px;
overflow: hidden;
}
.katex .base {
display: inline-block;
}
.katex .strut {
display: inline-block;
}
.katex .mathit {
font-family: KaTeX_Math;
font-style: italic;
}
.katex .mathbf {
font-family: KaTeX_Main;
font-weight: bold;
}
.katex .amsrm {
font-family: KaTeX_AMS;
}
.katex .mathbb {
font-family: KaTeX_AMS;
}
.katex .mathcal {
font-family: KaTeX_Caligraphic;
}
.katex .mathfrak {
font-family: KaTeX_Fraktur;
}
.katex .mathtt {
font-family: KaTeX_Typewriter;
}
.katex .mathscr {
font-family: KaTeX_Script;
}
.katex .mathsf {
font-family: KaTeX_SansSerif;
}
.katex .mainit {
font-family: KaTeX_Main;
font-style: italic;
}
.katex .textstyle > .mord + .mop {
margin-left: 0.16667em;
}
.katex .textstyle > .mord + .mbin {
margin-left: 0.22222em;
}
.katex .textstyle > .mord + .mrel {
margin-left: 0.27778em;
}
.katex .textstyle > .mord + .minner {
margin-left: 0.16667em;
}
.katex .textstyle > .mop + .mord {
margin-left: 0.16667em;
}
.katex .textstyle > .mop + .mop {
margin-left: 0.16667em;
}
.katex .textstyle > .mop + .mrel {
margin-left: 0.27778em;
}
.katex .textstyle > .mop + .minner {
margin-left: 0.16667em;
}
.katex .textstyle > .mbin + .mord {
margin-left: 0.22222em;
}
.katex .textstyle > .mbin + .mop {
margin-left: 0.22222em;
}
.katex .textstyle > .mbin + .mopen {
margin-left: 0.22222em;
}
.katex .textstyle > .mbin + .minner {
margin-left: 0.22222em;
}
.katex .textstyle > .mrel + .mord {
margin-left: 0.27778em;
}
.katex .textstyle > .mrel + .mop {
margin-left: 0.27778em;
}
.katex .textstyle > .mrel + .mopen {
margin-left: 0.27778em;
}
.katex .textstyle > .mrel + .minner {
margin-left: 0.27778em;
}
.katex .textstyle > .mclose + .mop {
margin-left: 0.16667em;
}
.katex .textstyle > .mclose + .mbin {
margin-left: 0.22222em;
}
.katex .textstyle > .mclose + .mrel {
margin-left: 0.27778em;
}
.katex .textstyle > .mclose + .minner {
margin-left: 0.16667em;
}
.katex .textstyle > .mpunct + .mord {
margin-left: 0.16667em;
}
.katex .textstyle > .mpunct + .mop {
margin-left: 0.16667em;
}
.katex .textstyle > .mpunct + .mrel {
margin-left: 0.16667em;
}
.katex .textstyle > .mpunct + .mopen {
margin-left: 0.16667em;
}
.katex .textstyle > .mpunct + .mclose {
margin-left: 0.16667em;
}
.katex .textstyle > .mpunct + .mpunct {
margin-left: 0.16667em;
}
.katex .textstyle > .mpunct + .minner {
margin-left: 0.16667em;
}
.katex .textstyle > .minner + .mord {
margin-left: 0.16667em;
}
.katex .textstyle > .minner + .mop {
margin-left: 0.16667em;
}
.katex .textstyle > .minner + .mbin {
margin-left: 0.22222em;
}
.katex .textstyle > .minner + .mrel {
margin-left: 0.27778em;
}
.katex .textstyle > .minner + .mopen {
margin-left: 0.16667em;
}
.katex .textstyle > .minner + .mpunct {
margin-left: 0.16667em;
}
.katex .textstyle > .minner + .minner {
margin-left: 0.16667em;
}
.katex .mord + .mop {
margin-left: 0.16667em;
}
.katex .mop + .mord {
margin-left: 0.16667em;
}
.katex .mop + .mop {
margin-left: 0.16667em;
}
.katex .mclose + .mop {
margin-left: 0.16667em;
}
.katex .minner + .mop {
margin-left: 0.16667em;
}
.katex .reset-textstyle.textstyle {
font-size: 1em;
}
.katex .reset-textstyle.scriptstyle {
font-size: 0.7em;
}
.katex .reset-textstyle.scriptscriptstyle {
font-size: 0.5em;
}
.katex .reset-scriptstyle.textstyle {
font-size: 1.42857em;
}
.katex .reset-scriptstyle.scriptstyle {
font-size: 1em;
}
.katex .reset-scriptstyle.scriptscriptstyle {
font-size: 0.71429em;
}
.katex .reset-scriptscriptstyle.textstyle {
font-size: 2em;
}
.katex .reset-scriptscriptstyle.scriptstyle {
font-size: 1.4em;
}
.katex .reset-scriptscriptstyle.scriptscriptstyle {
font-size: 1em;
}
.katex .style-wrap {
position: relative;
}
.katex .vlist {
display: inline-block;
}
.katex .vlist > span {
display: block;
height: 0;
position: relative;
}
.katex .vlist > span > span {
display: inline-block;
}
.katex .vlist .baseline-fix {
display: inline-table;
table-layout: fixed;
}
.katex .msupsub {
text-align: left;
}
.katex .mfrac > span > span {
text-align: center;
}
.katex .mfrac .frac-line {
width: 100%;
}
.katex .mfrac .frac-line:before {
border-bottom-style: solid;
border-bottom-width: 1px;
content: "";
display: block;
}
.katex .mfrac .frac-line:after {
border-bottom-style: solid;
border-bottom-width: 0.04em;
content: "";
display: block;
margin-top: -1px;
}
.katex .mspace {
display: inline-block;
}
.katex .mspace.negativethinspace {
margin-left: -0.16667em;
}
.katex .mspace.thinspace {
width: 0.16667em;
}
.katex .mspace.mediumspace {
width: 0.22222em;
}
.katex .mspace.thickspace {
width: 0.27778em;
}
.katex .mspace.enspace {
width: 0.5em;
}
.katex .mspace.quad {
width: 1em;
}
.katex .mspace.qquad {
width: 2em;
}
.katex .llap,
.katex .rlap {
width: 0;
position: relative;
}
.katex .llap > .inner,
.katex .rlap > .inner {
position: absolute;
}
.katex .llap > .fix,
.katex .rlap > .fix {
display: inline-block;
}
.katex .llap > .inner {
right: 0;
}
.katex .rlap > .inner {
left: 0;
}
.katex .katex-logo .a {
font-size: 0.75em;
margin-left: -0.32em;
position: relative;
top: -0.2em;
}
.katex .katex-logo .t {
margin-left: -0.23em;
}
.katex .katex-logo .e {
margin-left: -0.1667em;
position: relative;
top: 0.2155em;
}
.katex .katex-logo .x {
margin-left: -0.125em;
}
.katex .rule {
display: inline-block;
border: solid 0;
position: relative;
}
.katex .overline .overline-line,
.katex .underline .underline-line {
width: 100%;
}
.katex .overline .overline-line:before,
.katex .underline .underline-line:before {
border-bottom-style: solid;
border-bottom-width: 1px;
content: "";
display: block;
}
.katex .overline .overline-line:after,
.katex .underline .underline-line:after {
border-bottom-style: solid;
border-bottom-width: 0.04em;
content: "";
display: block;
margin-top: -1px;
}
.katex .sqrt > .sqrt-sign {
position: relative;
}
.katex .sqrt .sqrt-line {
width: 100%;
}
.katex .sqrt .sqrt-line:before {
border-bottom-style: solid;
border-bottom-width: 1px;
content: "";
display: block;
}
.katex .sqrt .sqrt-line:after {
border-bottom-style: solid;
border-bottom-width: 0.04em;
content: "";
display: block;
margin-top: -1px;
}
.katex .sqrt > .root {
margin-left: 0.27777778em;
margin-right: -0.55555556em;
}
.katex .sizing,
.katex .fontsize-ensurer {
display: inline-block;
}
.katex .sizing.reset-size1.size1,
.katex .fontsize-ensurer.reset-size1.size1 {
font-size: 1em;
}
.katex .sizing.reset-size1.size2,
.katex .fontsize-ensurer.reset-size1.size2 {
font-size: 1.4em;
}
.katex .sizing.reset-size1.size3,
.katex .fontsize-ensurer.reset-size1.size3 {
font-size: 1.6em;
}
.katex .sizing.reset-size1.size4,
.katex .fontsize-ensurer.reset-size1.size4 {
font-size: 1.8em;
}
.katex .sizing.reset-size1.size5,
.katex .fontsize-ensurer.reset-size1.size5 {
font-size: 2em;
}
.katex .sizing.reset-size1.size6,
.katex .fontsize-ensurer.reset-size1.size6 {
font-size: 2.4em;
}
.katex .sizing.reset-size1.size7,
.katex .fontsize-ensurer.reset-size1.size7 {
font-size: 2.88em;
}
.katex .sizing.reset-size1.size8,
.katex .fontsize-ensurer.reset-size1.size8 {
font-size: 3.46em;
}
.katex .sizing.reset-size1.size9,
.katex .fontsize-ensurer.reset-size1.size9 {
font-size: 4.14em;
}
.katex .sizing.reset-size1.size10,
.katex .fontsize-ensurer.reset-size1.size10 {
font-size: 4.98em;
}
.katex .sizing.reset-size2.size1,
.katex .fontsize-ensurer.reset-size2.size1 {
font-size: 0.71428571em;
}
.katex .sizing.reset-size2.size2,
.katex .fontsize-ensurer.reset-size2.size2 {
font-size: 1em;
}
.katex .sizing.reset-size2.size3,
.katex .fontsize-ensurer.reset-size2.size3 {
font-size: 1.14285714em;
}
.katex .sizing.reset-size2.size4,
.katex .fontsize-ensurer.reset-size2.size4 {
font-size: 1.28571429em;
}
.katex .sizing.reset-size2.size5,
.katex .fontsize-ensurer.reset-size2.size5 {
font-size: 1.42857143em;
}
.katex .sizing.reset-size2.size6,
.katex .fontsize-ensurer.reset-size2.size6 {
font-size: 1.71428571em;
}
.katex .sizing.reset-size2.size7,
.katex .fontsize-ensurer.reset-size2.size7 {
font-size: 2.05714286em;
}
.katex .sizing.reset-size2.size8,
.katex .fontsize-ensurer.reset-size2.size8 {
font-size: 2.47142857em;
}
.katex .sizing.reset-size2.size9,
.katex .fontsize-ensurer.reset-size2.size9 {
font-size: 2.95714286em;
}
.katex .sizing.reset-size2.size10,
.katex .fontsize-ensurer.reset-size2.size10 {
font-size: 3.55714286em;
}
.katex .sizing.reset-size3.size1,
.katex .fontsize-ensurer.reset-size3.size1 {
font-size: 0.625em;
}
.katex .sizing.reset-size3.size2,
.katex .fontsize-ensurer.reset-size3.size2 {
font-size: 0.875em;
}
.katex .sizing.reset-size3.size3,
.katex .fontsize-ensurer.reset-size3.size3 {
font-size: 1em;
}
.katex .sizing.reset-size3.size4,
.katex .fontsize-ensurer.reset-size3.size4 {
font-size: 1.125em;
}
.katex .sizing.reset-size3.size5,
.katex .fontsize-ensurer.reset-size3.size5 {
font-size: 1.25em;
}
.katex .sizing.reset-size3.size6,
.katex .fontsize-ensurer.reset-size3.size6 {
font-size: 1.5em;
}
.katex .sizing.reset-size3.size7,
.katex .fontsize-ensurer.reset-size3.size7 {
font-size: 1.8em;
}
.katex .sizing.reset-size3.size8,
.katex .fontsize-ensurer.reset-size3.size8 {
font-size: 2.1625em;
}
.katex .sizing.reset-size3.size9,
.katex .fontsize-ensurer.reset-size3.size9 {
font-size: 2.5875em;
}
.katex .sizing.reset-size3.size10,
.katex .fontsize-ensurer.reset-size3.size10 {
font-size: 3.1125em;
}
.katex .sizing.reset-size4.size1,
.katex .fontsize-ensurer.reset-size4.size1 {
font-size: 0.55555556em;
}
.katex .sizing.reset-size4.size2,
.katex .fontsize-ensurer.reset-size4.size2 {
font-size: 0.77777778em;
}
.katex .sizing.reset-size4.size3,
.katex .fontsize-ensurer.reset-size4.size3 {
font-size: 0.88888889em;
}
.katex .sizing.reset-size4.size4,
.katex .fontsize-ensurer.reset-size4.size4 {
font-size: 1em;
}
.katex .sizing.reset-size4.size5,
.katex .fontsize-ensurer.reset-size4.size5 {
font-size: 1.11111111em;
}
.katex .sizing.reset-size4.size6,
.katex .fontsize-ensurer.reset-size4.size6 {
font-size: 1.33333333em;
}
.katex .sizing.reset-size4.size7,
.katex .fontsize-ensurer.reset-size4.size7 {
font-size: 1.6em;
}
.katex .sizing.reset-size4.size8,
.katex .fontsize-ensurer.reset-size4.size8 {
font-size: 1.92222222em;
}
.katex .sizing.reset-size4.size9,
.katex .fontsize-ensurer.reset-size4.size9 {
font-size: 2.3em;
}
.katex .sizing.reset-size4.size10,
.katex .fontsize-ensurer.reset-size4.size10 {
font-size: 2.76666667em;
}
.katex .sizing.reset-size5.size1,
.katex .fontsize-ensurer.reset-size5.size1 {
font-size: 0.5em;
}
.katex .sizing.reset-size5.size2,
.katex .fontsize-ensurer.reset-size5.size2 {
font-size: 0.7em;
}
.katex .sizing.reset-size5.size3,
.katex .fontsize-ensurer.reset-size5.size3 {
font-size: 0.8em;
}
.katex .sizing.reset-size5.size4,
.katex .fontsize-ensurer.reset-size5.size4 {
font-size: 0.9em;
}
.katex .sizing.reset-size5.size5,
.katex .fontsize-ensurer.reset-size5.size5 {
font-size: 1em;
}
.katex .sizing.reset-size5.size6,
.katex .fontsize-ensurer.reset-size5.size6 {
font-size: 1.2em;
}
.katex .sizing.reset-size5.size7,
.katex .fontsize-ensurer.reset-size5.size7 {
font-size: 1.44em;
}
.katex .sizing.reset-size5.size8,
.katex .fontsize-ensurer.reset-size5.size8 {
font-size: 1.73em;
}
.katex .sizing.reset-size5.size9,
.katex .fontsize-ensurer.reset-size5.size9 {
font-size: 2.07em;
}
.katex .sizing.reset-size5.size10,
.katex .fontsize-ensurer.reset-size5.size10 {
font-size: 2.49em;
}
.katex .sizing.reset-size6.size1,
.katex .fontsize-ensurer.reset-size6.size1 {
font-size: 0.41666667em;
}
.katex .sizing.reset-size6.size2,
.katex .fontsize-ensurer.reset-size6.size2 {
font-size: 0.58333333em;
}
.katex .sizing.reset-size6.size3,
.katex .fontsize-ensurer.reset-size6.size3 {
font-size: 0.66666667em;
}
.katex .sizing.reset-size6.size4,
.katex .fontsize-ensurer.reset-size6.size4 {
font-size: 0.75em;
}
.katex .sizing.reset-size6.size5,
.katex .fontsize-ensurer.reset-size6.size5 {
font-size: 0.83333333em;
}
.katex .sizing.reset-size6.size6,
.katex .fontsize-ensurer.reset-size6.size6 {
font-size: 1em;
}
.katex .sizing.reset-size6.size7,
.katex .fontsize-ensurer.reset-size6.size7 {
font-size: 1.2em;
}
.katex .sizing.reset-size6.size8,
.katex .fontsize-ensurer.reset-size6.size8 {
font-size: 1.44166667em;
}
.katex .sizing.reset-size6.size9,
.katex .fontsize-ensurer.reset-size6.size9 {
font-size: 1.725em;
}
.katex .sizing.reset-size6.size10,
.katex .fontsize-ensurer.reset-size6.size10 {
font-size: 2.075em;
}
.katex .sizing.reset-size7.size1,
.katex .fontsize-ensurer.reset-size7.size1 {
font-size: 0.34722222em;
}
.katex .sizing.reset-size7.size2,
.katex .fontsize-ensurer.reset-size7.size2 {
font-size: 0.48611111em;
}
.katex .sizing.reset-size7.size3,
.katex .fontsize-ensurer.reset-size7.size3 {
font-size: 0.55555556em;
}
.katex .sizing.reset-size7.size4,
.katex .fontsize-ensurer.reset-size7.size4 {
font-size: 0.625em;
}
.katex .sizing.reset-size7.size5,
.katex .fontsize-ensurer.reset-size7.size5 {
font-size: 0.69444444em;
}
.katex .sizing.reset-size7.size6,
.katex .fontsize-ensurer.reset-size7.size6 {
font-size: 0.83333333em;
}
.katex .sizing.reset-size7.size7,
.katex .fontsize-ensurer.reset-size7.size7 {
font-size: 1em;
}
.katex .sizing.reset-size7.size8,
.katex .fontsize-ensurer.reset-size7.size8 {
font-size: 1.20138889em;
}
.katex .sizing.reset-size7.size9,
.katex .fontsize-ensurer.reset-size7.size9 {
font-size: 1.4375em;
}
.katex .sizing.reset-size7.size10,
.katex .fontsize-ensurer.reset-size7.size10 {
font-size: 1.72916667em;
}
.katex .sizing.reset-size8.size1,
.katex .fontsize-ensurer.reset-size8.size1 {
font-size: 0.28901734em;
}
.katex .sizing.reset-size8.size2,
.katex .fontsize-ensurer.reset-size8.size2 {
font-size: 0.40462428em;
}
.katex .sizing.reset-size8.size3,
.katex .fontsize-ensurer.reset-size8.size3 {
font-size: 0.46242775em;
}
.katex .sizing.reset-size8.size4,
.katex .fontsize-ensurer.reset-size8.size4 {
font-size: 0.52023121em;
}
.katex .sizing.reset-size8.size5,
.katex .fontsize-ensurer.reset-size8.size5 {
font-size: 0.57803468em;
}
.katex .sizing.reset-size8.size6,
.katex .fontsize-ensurer.reset-size8.size6 {
font-size: 0.69364162em;
}
.katex .sizing.reset-size8.size7,
.katex .fontsize-ensurer.reset-size8.size7 {
font-size: 0.83236994em;
}
.katex .sizing.reset-size8.size8,
.katex .fontsize-ensurer.reset-size8.size8 {
font-size: 1em;
}
.katex .sizing.reset-size8.size9,
.katex .fontsize-ensurer.reset-size8.size9 {
font-size: 1.19653179em;
}
.katex .sizing.reset-size8.size10,
.katex .fontsize-ensurer.reset-size8.size10 {
font-size: 1.43930636em;
}
.katex .sizing.reset-size9.size1,
.katex .fontsize-ensurer.reset-size9.size1 {
font-size: 0.24154589em;
}
.katex .sizing.reset-size9.size2,
.katex .fontsize-ensurer.reset-size9.size2 {
font-size: 0.33816425em;
}
.katex .sizing.reset-size9.size3,
.katex .fontsize-ensurer.reset-size9.size3 {
font-size: 0.38647343em;
}
.katex .sizing.reset-size9.size4,
.katex .fontsize-ensurer.reset-size9.size4 {
font-size: 0.43478261em;
}
.katex .sizing.reset-size9.size5,
.katex .fontsize-ensurer.reset-size9.size5 {
font-size: 0.48309179em;
}
.katex .sizing.reset-size9.size6,
.katex .fontsize-ensurer.reset-size9.size6 {
font-size: 0.57971014em;
}
.katex .sizing.reset-size9.size7,
.katex .fontsize-ensurer.reset-size9.size7 {
font-size: 0.69565217em;
}
.katex .sizing.reset-size9.size8,
.katex .fontsize-ensurer.reset-size9.size8 {
font-size: 0.83574879em;
}
.katex .sizing.reset-size9.size9,
.katex .fontsize-ensurer.reset-size9.size9 {
font-size: 1em;
}
.katex .sizing.reset-size9.size10,
.katex .fontsize-ensurer.reset-size9.size10 {
font-size: 1.20289855em;
}
.katex .sizing.reset-size10.size1,
.katex .fontsize-ensurer.reset-size10.size1 {
font-size: 0.20080321em;
}
.katex .sizing.reset-size10.size2,
.katex .fontsize-ensurer.reset-size10.size2 {
font-size: 0.2811245em;
}
.katex .sizing.reset-size10.size3,
.katex .fontsize-ensurer.reset-size10.size3 {
font-size: 0.32128514em;
}
.katex .sizing.reset-size10.size4,
.katex .fontsize-ensurer.reset-size10.size4 {
font-size: 0.36144578em;
}
.katex .sizing.reset-size10.size5,
.katex .fontsize-ensurer.reset-size10.size5 {
font-size: 0.40160643em;
}
.katex .sizing.reset-size10.size6,
.katex .fontsize-ensurer.reset-size10.size6 {
font-size: 0.48192771em;
}
.katex .sizing.reset-size10.size7,
.katex .fontsize-ensurer.reset-size10.size7 {
font-size: 0.57831325em;
}
.katex .sizing.reset-size10.size8,
.katex .fontsize-ensurer.reset-size10.size8 {
font-size: 0.69477912em;
}
.katex .sizing.reset-size10.size9,
.katex .fontsize-ensurer.reset-size10.size9 {
font-size: 0.8313253em;
}
.katex .sizing.reset-size10.size10,
.katex .fontsize-ensurer.reset-size10.size10 {
font-size: 1em;
}
.katex .delimsizing.size1 {
font-family: KaTeX_Size1;
}
.katex .delimsizing.size2 {
font-family: KaTeX_Size2;
}
.katex .delimsizing.size3 {
font-family: KaTeX_Size3;
}
.katex .delimsizing.size4 {
font-family: KaTeX_Size4;
}
.katex .delimsizing.mult .delim-size1 > span {
font-family: KaTeX_Size1;
}
.katex .delimsizing.mult .delim-size4 > span {
font-family: KaTeX_Size4;
}
.katex .nulldelimiter {
display: inline-block;
width: 0.12em;
}
.katex .op-symbol {
position: relative;
}
.katex .op-symbol.small-op {
font-family: KaTeX_Size1;
}
.katex .op-symbol.large-op {
font-family: KaTeX_Size2;
}
.katex .op-limits > .vlist > span {
text-align: center;
}
.katex .accent > .vlist > span {
text-align: center;
}
.katex .accent .accent-body > span {
width: 0;
}
.katex .accent .accent-body.accent-vec > span {
position: relative;
left: 0.326em;
}
.katex .mtable .vertical-separator {
display: inline-block;
margin: 0 -0.025em;
border-right: 0.05em solid black;
}
.katex .mtable .arraycolsep {
display: inline-block;
}
.katex .mtable .col-align-c > .vlist {
text-align: center;
}
.katex .mtable .col-align-l > .vlist {
text-align: left;
}
.katex .mtable .col-align-r > .vlist {
text-align: right;
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment