diff --git a/.gitignore b/.gitignore index bfe173fb..62ed1768 100644 --- a/.gitignore +++ b/.gitignore @@ -16,3 +16,10 @@ diff.png /test/symgroups.pdf /test/screenshotter/unicode-fonts coverage/ +lib/core/metadata.js +lib/core/MetadataBlog.js +website/translated_docs +website/build/ +website/yarn.lock +website/node_modules +website/i18n/* diff --git a/README.md b/README.md index c8bed79f..fa83bf99 100644 --- a/README.md +++ b/README.md @@ -14,9 +14,9 @@ KaTeX is a fast, easy-to-use JavaScript library for TeX math rendering on the we KaTeX supports all major browsers, including Chrome, Safari, Firefox, Opera, Edge, and IE 9 - IE 11. More information can be found on the [list of supported commands](https://khan.github.io/KaTeX/function-support.html) and on the [wiki](https://github.com/khan/katex/wiki). -## Usage +## Getting started -You can [download KaTeX](https://github.com/khan/katex/releases) and host it on your server or include the `katex.min.js` and `katex.min.css` files on your page directly from a CDN: +[Download KaTeX](https://github.com/khan/katex/releases) and host it on your server or include the `katex.min.js` and `katex.min.css` files on your page directly from a CDN: ```html @@ -51,127 +51,9 @@ var html = katex.renderToString("c = \\pm\\sqrt{a^2 + b^2}"); Make sure to include the CSS and font files, but there is no need to include the JavaScript. Like `render`, `renderToString` throws if it can't parse the expression. -#### Security +## Documentation -Any HTML generated by KaTeX *should* be safe from ` + +``` + +Then, call the exposed `renderMathInElement` function in a script tag +before the close body tag: + +```html + + ... + + +``` + +If you prefer to have all your setup inside the html ``, +you can use the following script there +(instead of the one above at the end of the ``): + +```html + + ... + + ... + +``` + +## API +This extension exposes a single function, `window.renderMathInElement`, with +the following API: + +```js +function renderMathInElement(elem, options) +``` + +`elem` is an HTML DOM element. The function will recursively search for text +nodes inside this element and render the math in them. + +`options` is an optional object argument that can have the same keys as [the +object passed to `katex.render`](https://github.com/Khan/KaTeX/#rendering-options), +in addition to two auto-render-specific keys: + +- `delimiters`: This is a list of delimiters to look for math. Each delimiter + has three properties: + + - `left`: A string which starts the math expression (i.e. the left delimiter). + - `right`: A string which ends the math expression (i.e. the right delimiter). + - `display`: A boolean of whether the math in the expression should be + rendered in display mode or not. + + The default value is: + + ```js + [ + {left: "$$", right: "$$", display: true}, + {left: "\\(", right: "\\)", display: false}, + {left: "\\[", right: "\\]", display: true} + ] + ``` + +- `ignoredTags`: This is a list of DOM node types to ignore when recursing + through. The default value is + `["script", "noscript", "style", "textarea", "pre", "code"]`. + +- `errorCallback`: A callback method returning a message and an error stack + in case of an critical error during rendering. The default uses `console.error`. + +The `displayMode` property of the options object is ignored, and is +instead taken from the `display` key of the corresponding entry in the +`delimiters` key. + +The same `options.macros` object (which defaults to an empty object `{}`) +is passed into several calls to `katex.render`, so that consecutive equations +can build up shared macros by `\gdef`. diff --git a/docs/browser.md b/docs/browser.md new file mode 100644 index 00000000..f52d5c6b --- /dev/null +++ b/docs/browser.md @@ -0,0 +1,31 @@ +--- +id: browser +title: Browser +--- +> KaTeX supports all major browsers, including Chrome, Safari, Firefox, Opera, Edge, and IE 9 - IE 11. + +## CDN(Content Delivery Network) +Use CDN to deliver KaTeX to your project: + +```html + + +``` + +KaTeX also provides minified versions: + +```html + + +``` + +## Download & Host Things Yourself +Download the latest version from [here](https://github.com/Khan/KaTeX/releases), +copy `katex.js`, `katex.css`(or `katex.min.js` and `katex.min.css` to use minified +versions) and `fonts` from `/katex`, and include like above. + +## Bundler +Use [`Node.js` package managers](node.md) to install KaTeX and require it in your +project. Then bundle using bundlers like [webpack](https://webpack.js.org/) or +[rollup.js](https://rollupjs.org/). Note that you have to bundle the stylesheet(`katex.css`) +or include it manually. diff --git a/docs/cli.md b/docs/cli.md new file mode 100644 index 00000000..57820d7e --- /dev/null +++ b/docs/cli.md @@ -0,0 +1,58 @@ +--- +id: cli +title: CLI +--- + +KaTeX installed [using Node.js package managers](node.md) comes with a built-in CLI +which can be used to render TeX to HTML from the command line. By default, CLI will +take the input from `stdin`. + +```bash +npx katex +``` + +> Above uses the `npx` command to run the locally installed executable. +You can execute with the relative path: `./node_modules/.bin/katex` + +> To use CLI from local clone, you need to build the project first by +running `npm run dist` + +# Usage + +## `-d, --display-mode` +If true the math will be rendered in display mode, which will put the math in +display style (so `\int` and `\sum` are large, for example), and will center the +math on the page on its own line. [false] + +## `-t, --no-throw-on-error` +If true, KaTeX will throw a ParseError when it encounters an unsupported command. +If false, KaTeX will render the unsupported command as text in the color given by +errorColor. [true] + +## `-c color, --error-color color` +A color string given in the format 'rgb' or 'rrggbb'. This option determines the +color which unsupported commands are rendered in. [#cc0000] + +## `-b, --color-is-text-color` +Makes \color behave like LaTeX's 2-argument \textcolor, instead of LaTeX's +one-argument \color mode change. [false] + +## `-u, --unicode-text-in-math-mode` +Add support for unicode text characters in math mode. [false] + +## `-s size, --max-size size` +If non-zero, all user-specified sizes, e.g. in \rule{500em}{500em}, will be capped +to maxSize ems. Otherwise, elements and spaces can be arbitrarily large [0] + +## `-m macro:expansion, --macro macro:expansion` +A custom macro. Each macro is a property with a name like \name which maps to a +string that describes the expansion of the macro. [] + +## `-f path, --macro-file path` +Read macro definitions from the given file. + +## `-i path, --input path` +Read LaTeX input from the given file. + +## `-o path, --output path` +Write html output to the given file. diff --git a/docs/error.md b/docs/error.md new file mode 100644 index 00000000..c8fa654e --- /dev/null +++ b/docs/error.md @@ -0,0 +1,17 @@ +--- +id: error +title: Handling Errors +--- +If KaTeX encounters an error (invalid or unsupported LaTeX) and `throwOnError` +hasn't been set to `false`, then it will throw an exception of type +`ParseError`. The message in this error includes some of the LaTeX +source code, so needs to be escaped if you want to render it to HTML. + +In particular, you should convert `&`, `<`, `>` characters to +`&`, `<`, `>` (e.g., using `_.escape`) +before including either LaTeX source code or exception messages in your +HTML/DOM. (Failure to escape in this way makes a ` + + + + + + + +
+

KaTeX

+
+ The fastest math typesetting library for the web. +
+ +
+
+
+ +
+ +
+
+
+ See how it renders with KaTeX: +
+
+
+
+
+ +
+
+ Simple API, no dependencies – yet super-fast on all major browsers. +
+ + + +
+
+
+
+
+ +
+ Lightning-fast, even on pages with hundreds of expressions: +
+ +
+
+
MathJax
+
+
+ + +
+ + + + + + + + diff --git a/website/remarkableKatex.js b/website/remarkableKatex.js new file mode 100644 index 00000000..f43afffb --- /dev/null +++ b/website/remarkableKatex.js @@ -0,0 +1,183 @@ +/* MIT License + +Copyright (c) 2017 Brad Howes + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. +*/ + +/** + * Plugin for Remarkable Markdown processor which transforms $..$ and $$..$$ sequences into math HTML using the + * Katex package. + */ +module.exports = function(md, options) { + var katex = require("../"); + + function renderKatex(source, displayMode) { + return katex.renderToString(source, {displayMode: displayMode, throwOnError: false}); + } + + /** + * Parse '$$' as a block. I don't think this is needed since it is already done in the parseInlineKatex + * method. Based off of similar method in remarkable. + */ + function parseBlockKatex(state, startLine, endLine) { + var marker, len, params, nextLine, mem, + haveEndMarker = false, + pos = state.bMarks[startLine] + state.tShift[startLine], + max = state.eMarks[startLine]; + var dollar = 0x24; + + if (pos + 1 > max) { return false; } + + marker = state.src.charCodeAt(pos); + if (marker !== dollar) { return false; } + + // scan marker length + mem = pos; + pos = state.skipChars(pos, marker); + len = pos - mem; + + if (len != 2) { return false; } + + // search end of block + nextLine = startLine; + + for (;;) { + ++nextLine; + if (nextLine >= endLine) { + + // unclosed block should be autoclosed by end of document. + // also block seems to be autoclosed by end of parent + break; + } + + pos = mem = state.bMarks[nextLine] + state.tShift[nextLine]; + max = state.eMarks[nextLine]; + + if (pos < max && state.tShift[nextLine] < state.blkIndent) { + + // non-empty line with negative indent should stop the list: + // - ``` + // test + break; + } + + if (state.src.charCodeAt(pos) !== dollar) { continue }; + + if (state.tShift[nextLine] - state.blkIndent >= 4) { + + // closing fence should be indented less than 4 spaces + continue; + } + + pos = state.skipChars(pos, marker); + + // closing code fence must be at least as long as the opening one + if (pos - mem < len) { continue; } + + // make sure tail has spaces only + pos = state.skipSpaces(pos); + + if (pos < max) { continue; } + + haveEndMarker = true; + + // found! + break; + } + + // If a fence has heading spaces, they should be removed from its inner block + len = state.tShift[startLine]; + + state.line = nextLine + (haveEndMarker ? 1 : 0); + + var content = state.getLines(startLine + 1, nextLine, len, true) + .replace(/[ \n]+/g, ' ') + .trim(); + + state.tokens.push({ + type: 'katex', + params: params, + content: content, + lines: [startLine, state.line], + level: state.level, + block: true + }); + + return true; + } + + /** + * Look for '$' or '$$' spans in Markdown text. Based off of the 'fenced' parser in remarkable. + */ + function parseInlineKatex(state, silent) { + var dollar = 0x24; + var pos = state.pos; + var start = pos, max = state.posMax, marker, matchStart, matchEnd ; + + if (state.src.charCodeAt(pos) !== dollar) { return false; } + ++pos; + + while (pos < max && state.src.charCodeAt(pos) === dollar) { + ++pos; + } + + marker = state.src.slice(start, pos); + if (marker.length > 2) { return false; } + + matchStart = matchEnd = pos; + + while ((matchStart = state.src.indexOf('$', matchEnd)) !== -1) { + matchEnd = matchStart + 1; + + while (matchEnd < max && state.src.charCodeAt(matchEnd) === dollar) { + ++matchEnd; + } + + if (matchEnd - matchStart == marker.length) { + if (!silent) { + var content = state.src.slice(pos, matchStart) + .replace(/[ \n]+/g, ' ') + .trim(); + + state.push({ + type: 'katex', + content: content, + block: marker.length > 1, + level: state.level + }); + } + + state.pos = matchEnd; + return true; + } + } + + if (! silent) state.pending += marker; + state.pos += marker.length; + + return true; + } + + md.inline.ruler.push('katex', parseInlineKatex, options); + md.block.ruler.push('katex', parseBlockKatex, options); + md.renderer.rules.katex = function(tokens, idx) { + return renderKatex(tokens[idx].content, tokens[idx].block); + }; +}; diff --git a/website/sidebars.json b/website/sidebars.json new file mode 100644 index 00000000..82f69785 --- /dev/null +++ b/website/sidebars.json @@ -0,0 +1,8 @@ +{ + "docs": { + "Installation": ["node", "browser"], + "Usage": ["api", "cli", "autorender", "libs"], + "Configuring KaTeX": ["options", "security", "error", "font"], + "Misc": ["supported", "issues"] + } +} diff --git a/website/siteConfig.js b/website/siteConfig.js new file mode 100644 index 00000000..f840b536 --- /dev/null +++ b/website/siteConfig.js @@ -0,0 +1,84 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +// See https://docusaurus.io/docs/site-config.html for all the possible +// site configuration options. + +/* List of projects/orgs using your project for the users page */ +const users = [ + { + caption: 'GitLab', + image: 'https://gitlab.com/gitlab-com/gitlab-artwork/raw/master/logo/logo-square.png', + infoLink: 'https://gitlab.com/', + pinned: true, + }, +]; + +const siteConfig = { + title: 'KaTeX', + tagline: 'The fastest math typesetting library for the web', + url: 'https://khan.github.io', + baseUrl: '/KaTeX/', + + // Used for publishing and more + projectName: 'KaTeX', + organizationName: 'Khan', + + headerLinks: [ + {doc: 'node', label: 'Docs'}, + {page: 'users', label: 'Users'}, + {href: 'https://github.com/Khan/KaTeX', label: 'GitHub'}, + {search: true}, + ], + + // If you have users set above, you add it here: + users, + + /* path to images for header/footer */ + headerIcon: 'img/katex-logo.svg', + footerIcon: 'img/katex-logo.svg', + favicon: 'https://khan.github.io/favicon.ico', + + disableHeaderTitle: true, + + /* colors for website */ + colors: { + primaryColor: '#329894', + secondaryColor: '#266e6c', + }, + + // This copyright info is used in /core/Footer.js and blog rss/atom feeds. + copyright: + 'Copyright © ' + + new Date().getFullYear() + + ' Khan Academy', + + highlight: { + // Highlight.js theme to use for syntax highlighting in code blocks + theme: 'default', + }, + + markdownPlugins: [function(md) { + md.use(require('./remarkableKatex')); + }], + + scripts: ['https://buttons.github.io/buttons.js'], + stylesheets: ['https://cdn.jsdelivr.net/npm/katex@0.10.0-beta/dist/katex.min.css'], + + separateCss: ['static/static', 'static\\static'], + + /* On page navigation for the current documentation page */ + onPageNav: 'separate', + + /* Open Graph and Twitter card images */ + ogImage: 'img/og_logo.png', + twitterImage: 'img/og_logo.png', + + repoUrl: 'https://github.com/Khan/KaTeX', +}; + +module.exports = siteConfig; diff --git a/website/static/css/custom.css b/website/static/css/custom.css new file mode 100644 index 00000000..b06a9ee9 --- /dev/null +++ b/website/static/css/custom.css @@ -0,0 +1,18 @@ +.fixedHeaderContainer header img { + height: 80%; +} + +@media only screen and (min-device-width: 360px) and (max-device-width: 736px) { +} + +@media only screen and (min-width: 1024px) { +} + +@media only screen and (max-width: 1023px) { +} + +@media only screen and (min-width: 1400px) { +} + +@media only screen and (min-width: 1500px) { +} diff --git a/website/static/img/katex-comparison.gif b/website/static/img/katex-comparison.gif new file mode 100644 index 00000000..c62f2893 Binary files /dev/null and b/website/static/img/katex-comparison.gif differ diff --git a/website/static/img/katex-logo.svg b/website/static/img/katex-logo.svg new file mode 100644 index 00000000..9c3c0611 --- /dev/null +++ b/website/static/img/katex-logo.svg @@ -0,0 +1,67 @@ + + + + + + + + + + + + diff --git a/website/static/img/khan-academy.png b/website/static/img/khan-academy.png new file mode 100644 index 00000000..7f9f09fd Binary files /dev/null and b/website/static/img/khan-academy.png differ diff --git a/website/static/img/og_logo.png b/website/static/img/og_logo.png new file mode 100644 index 00000000..8a926886 Binary files /dev/null and b/website/static/img/og_logo.png differ diff --git a/website/static/js/index.js b/website/static/js/index.js new file mode 100644 index 00000000..70548e59 --- /dev/null +++ b/website/static/js/index.js @@ -0,0 +1,32 @@ +window.startup = function() { + var tex = document.getElementsByClassName("tex"); + Array.prototype.forEach.call(tex, function(el) { + katex.render(el.getAttribute("data-expr"), el); + }); + + var demoInput = document.getElementById("demo-input"); + var demoOutput = document.getElementById("demo-output"); + + function doDemo() { + try { + katex.render(demoInput.value, demoOutput, { + displayMode: true + }); + } catch(err) { + while(demoOutput.lastChild) { + demoOutput.removeChild(demoOutput.lastChild); + } + var msg = document.createTextNode(err.message); + var span = document.createElement("span"); + span.appendChild(msg); + demoOutput.appendChild(span); + span.setAttribute("class", "errorMessage"); + } + } + + demoInput.addEventListener("input", function() { + doDemo(); + }); + + doDemo(); +}; diff --git a/website/static/static/index.css b/website/static/static/index.css new file mode 100644 index 00000000..89477dc8 --- /dev/null +++ b/website/static/static/index.css @@ -0,0 +1,212 @@ +body { + font: 16px/1.4 "proxima-nova", "Helvetica Neue", Arial, Helvetica, sans-serif; +} + +.container { + margin: 0 auto; + margin: 0 auto; + width: 800px; +} + +.header, .footer { + color: #fff; + background: #329894; + overflow: hidden; +} + +.cta { + margin: 10px 0; + text-align: center; +} + +.cta a { + border-radius: 3px; + display: inline-block; + font-weight: 600; + margin: 0 10px; + padding: 8px 24px; + text-decoration: none; +} + +.header { + padding: 40px 0 0; +} + +.logo { + color: #fff; + font-size: 45px; + margin: 0; +} + +.tagline { + font-size: 28px; + font-weight: 300; + padding: 20px 0; + text-align: center; +} + +.tagline em { + font-weight: 600; +} + +.header .cta a { + border: 3px solid; + color: #fff; +} + +.header .cta a:hover { + background: rgba(255, 255, 255, 0.1); +} + +.demo { + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + font-size: 22px; + margin: 50px 0 0; +} + +.demo, +.demo-left, +.demo-right { + height: 260px; +} + +.demo-left, +.demo-right { + box-sizing: border-box; + float: left; + padding: 20px 30px; + width: 50%; +} + +.demo-instructions { + line-height: 1.5; + margin-bottom: 10px; + opacity: 0.8; +} + +.demo-left { + background: #444; +} + +.demo-left textarea { + background: #000; + border: none; + box-sizing: border-box; + color: #fff; + font: 14px Menlo, monospace; + height: 160px; + padding: 10px; + resize: none; + width: 100%; +} + +.demo-right { + background: #fff; + color: #444; +} + +#demo-output { + font-size: 18px; + padding: 10px 0; + overflow: auto; +} + +.main { + background: #fff2e7; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + min-height: 400px; + position: relative; + padding: 30px 0; +} + +.subtagline { + color: #329894; + font-size: 24px; + font-weight: 600; + padding: 20px 0; + text-align: center; +} + +.features { + font-size: 20px; + margin: 0 auto; + width: 540px; +} + +.features > li { + margin: 10px 0; +} + +.main .examples { + margin: 40px 0; +} + +.main .example { + font-size: 15px; + text-align: center; + margin: 30px 0; +} + +.main .comparison-labels { + width: 100%; + height: 40px; +} + +.main .comparison-labels .left, +.main .comparison-labels .right { + box-sizing: border-box; + width: 50%; + float: left; + height: 40px; + text-align: center; + font-size: 20px; +} + +.main .comparison { + background-image: url(../../img/katex-comparison.gif); + background-position: 50% 50%; + background-size: cover; + box-shadow: 0 0 10px rgba(0, 0, 0, 0.3); + height: 340px; + width: 100%; +} + +.main .cta { + padding-top: 50px; +} + +.main .cta a { + border: 3px solid; + color: #329894; +} + +.main .cta a:hover { + background: rgba(50, 152, 148, 0.1); +} + +.footer { + padding: 30px 0; + font-size: 21px; +} + +.ka-logo { + float: right; +} + +.ka-logo img { + border: none; + vertical-align: -1px; +} + +.credits { + float: left; + font-size: 16px; +} + +.credits a { + color: white; +} + +.errorMessage { + color: red; +}