@@ -52,8 +91,8 @@ Try it at [mathlive.io](https://mathlive.io)
### To display math
-You can use MathLive to simply render math equations by
-[adding a few lines to your web page](tutorials/USAGE_GUIDE.md).
+You can use MathLive to simply render math equations by
+[adding a few lines to your web page](tutorials/USAGE_GUIDE.md).
```html
@@ -65,9 +104,9 @@ You can use MathLive to simply render math equations by
Euler's Identity
-
$$e^{i\pi} + 1 = 0$$
+
$$e^{i\pi} + 1 = 0$$
-
@@ -76,7 +115,7 @@ You can use MathLive to simply render math equations by
### To edit math
-You can also incorporate a “math field” to edit math just like you would edit
+You can also incorporate a “math field” to edit math just like you would edit
text. The MathLive APIs allow you to interact with the math field,
including extracting its content, inserting placeholders and more.
@@ -90,7 +129,7 @@ including extracting its content, inserting placeholders and more.
f(x)=
-
@@ -110,7 +149,7 @@ However, if you:
- want to contribute to MathLive
- use your own CDN
- make some other changes to MathLive
-you can also install it locally in your project.
+you can also install it locally in your project.
To do so:
```bash
@@ -123,16 +162,16 @@ This will make a local build of MathLive, run a local HTTP server and open a pag
* Something wrong? Got ideas for new features? Write up an issue. Read about
[Contributing](CONTRIBUTING.md) and follow our [Code of Conduct](CODE_OF_CONDUCT.md)
-* Want to use MathLive in your web page? The [Usage Guide](tutorials/USAGE_GUIDE.md)
+* Want to use MathLive in your web page? The [Usage Guide](tutorials/USAGE_GUIDE.md)
has all the details.
-* Want to contribute some code for an issue or a feature? Read the
-[Contributor Guide](tutorials/CONTRIBUTOR_GUIDE.md) and the
+* Want to contribute some code for an issue or a feature? Read the
+[Contributor Guide](tutorials/CONTRIBUTOR_GUIDE.md) and the
[docs](http://docs.mathlive.io). Looking for inspiration? Pick one of
the [good first issues](https://github.com/arnog/mathlive/labels/good%20first%20issue)
## More Questions?
-* Join our Slack channel at https://mathlive.slack.com.
+* Join our Slack channel at https://mathlive.slack.com.
* Drop a line to arno@arno.org or [/u/real_arnog](https://www.reddit.com/user/real_arnog)
## License
diff --git a/dist/addons/auto-render.js b/dist/addons/auto-render.js
new file mode 100755
index 000000000..26d00086d
--- /dev/null
+++ b/dist/addons/auto-render.js
@@ -0,0 +1,409 @@
+"use strict";
+
+Object.defineProperty(exports, "__esModule", {
+ value: true
+});
+exports.default = void 0;
+
+require("../core/mathAtom.js");
+
+var _definitions = _interopRequireDefault(require("../core/definitions.js"));
+
+function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
+
+/* eslint no-console:0 */
+// eslint-disable-line no-unused-vars
+function findEndOfMath(delimiter, text, startIndex) {
+ // Adapted from
+ // https://github.com/Khan/perseus/blob/master/src/perseus-markdown.jsx
+ var index = startIndex;
+ var braceLevel = 0;
+ var delimLength = delimiter.length;
+
+ while (index < text.length) {
+ var character = text[index];
+
+ if (braceLevel <= 0 && text.slice(index, index + delimLength) === delimiter) {
+ return index;
+ } else if (character === '\\') {
+ index++;
+ } else if (character === '{') {
+ braceLevel++;
+ } else if (character === '}') {
+ braceLevel--;
+ }
+
+ index++;
+ }
+
+ return -1;
+}
+
+function splitAtDelimiters(startData, leftDelim, rightDelim, mathstyle) {
+ var finalData = [];
+
+ for (var i = 0; i < startData.length; i++) {
+ if (startData[i].type === 'text') {
+ var text = startData[i].data;
+ var lookingForLeft = true;
+ var currIndex = 0;
+ var nextIndex = void 0;
+ nextIndex = text.indexOf(leftDelim);
+
+ if (nextIndex !== -1) {
+ currIndex = nextIndex;
+
+ if (currIndex > 0) {
+ finalData.push({
+ type: 'text',
+ data: text.slice(0, currIndex)
+ });
+ }
+
+ lookingForLeft = false;
+ }
+
+ var done = false;
+
+ while (!done) {
+ if (lookingForLeft) {
+ nextIndex = text.indexOf(leftDelim, currIndex);
+
+ if (nextIndex === -1) {
+ done = true;
+ break;
+ }
+
+ if (currIndex !== nextIndex) {
+ finalData.push({
+ type: 'text',
+ data: text.slice(currIndex, nextIndex)
+ });
+ }
+
+ currIndex = nextIndex;
+ } else {
+ nextIndex = findEndOfMath(rightDelim, text, currIndex + leftDelim.length);
+
+ if (nextIndex === -1) {
+ done = true;
+ break;
+ }
+
+ finalData.push({
+ type: 'math',
+ data: text.slice(currIndex + leftDelim.length, nextIndex),
+ rawData: text.slice(currIndex, nextIndex + rightDelim.length),
+ mathstyle: mathstyle
+ });
+ currIndex = nextIndex + rightDelim.length;
+ }
+
+ lookingForLeft = !lookingForLeft;
+ }
+
+ if (currIndex < text.length) {
+ finalData.push({
+ type: 'text',
+ data: text.slice(currIndex)
+ });
+ }
+ } else {
+ finalData.push(startData[i]);
+ }
+ }
+
+ return finalData;
+}
+
+function splitWithDelimiters(text, delimiters) {
+ var data = [{
+ type: 'text',
+ data: text
+ }];
+
+ for (var i = 0; i < delimiters.inline.length; i++) {
+ var delimiter = delimiters.inline[i];
+ data = splitAtDelimiters(data, delimiter[0], delimiter[1], 'textstyle');
+ }
+
+ for (var _i = 0; _i < delimiters.display.length; _i++) {
+ var _delimiter = delimiters.display[_i];
+ data = splitAtDelimiters(data, _delimiter[0], _delimiter[1], 'displaystyle');
+ }
+
+ return data;
+}
+
+function createMathMLNode(latex, options) {
+ // Create a node for AT (Assistive Technology, e.g. screen reader) to speak, etc.
+ // This node has a style that makes it be invisible to display but is seen by AT
+ var span = document.createElement('span');
+
+ try {
+ span.innerHTML = "";
+ } catch (e) {
+ console.error('Could not convert\'' + latex + '\' to MathML with ', e);
+ span.textContent = latex;
+ }
+
+ span.className = 'sr-only';
+ return span;
+}
+
+function createMarkupNode(text, options, mathstyle, createNodeOnFailure) {
+ // Create a node for displaying math.
+ // This is slightly ugly because in the case of failure to create the markup,
+ // sometimes a text node is desired and sometimes not.
+ // 'createTextNodeOnFailure' controls this and null is returned when no node is created.
+ // This node is made invisible to AT (screen readers)
+ var span = document.createElement('span');
+ span.setAttribute('aria-hidden', 'true');
+
+ if (options.preserveOriginalContent) {
+ span.setAttribute('data-' + options.namespace + 'original-content', text);
+
+ if (mathstyle) {
+ span.setAttribute('data-' + options.namespace + 'original-mathstyle', mathstyle);
+ }
+ }
+
+ try {
+ span.innerHTML = options.renderToMarkup(text, mathstyle || 'displaystyle', 'html', options.macros);
+ } catch (e) {
+ console.error('Could not parse\'' + text + '\' with ', e);
+
+ if (createNodeOnFailure) {
+ span = document.createTextNode(text);
+ } else {
+ return null;
+ }
+ }
+
+ return span;
+}
+
+function createAccessibleMarkupPair(text, mathstyle, options, createNodeOnFailure) {
+ // Create a math node (a span with an accessible component and a visual component)
+ // If there is an error in parsing the latex, 'createNodeOnFailure' controls whether
+ // 'null' is returned or an accessible node with the text used.
+ var markupNode = createMarkupNode(text, options, mathstyle, createNodeOnFailure);
+
+ if (markupNode && /\b(mathml|speakable-text)\b/i.test(options.renderAccessibleContent)) {
+ var fragment = document.createDocumentFragment();
+
+ if (/\bmathml\b/i.test(options.renderAccessibleContent) && options.renderToMathML) {
+ fragment.appendChild(createMathMLNode(text, options));
+ }
+
+ if (/\bspeakable-text\b/i.test(options.renderAccessibleContent) && options.renderToSpeakableText) {
+ var span = document.createElement('span');
+ span.innerHTML = options.renderToSpeakableText(text, options);
+ span.className = 'sr-only';
+ fragment.appendChild(span);
+ }
+
+ fragment.appendChild(markupNode);
+ return fragment;
+ }
+
+ return markupNode;
+}
+
+function scanText(text, options) {
+ // If the text starts with '\begin'...
+ // (this is a MathJAX behavior)
+ var fragment = null;
+
+ if (options.TeX.processEnvironments && /^\s*\\begin/.test(text)) {
+ fragment = document.createDocumentFragment();
+ fragment.appendChild(createAccessibleMarkupPair(text, undefined, options, true));
+ } else {
+ var data = splitWithDelimiters(text, options.TeX.delimiters);
+
+ if (data.length === 1 && data[0].type === 'text') {
+ // This text contains no math. No need to continue processing
+ return null;
+ }
+
+ fragment = document.createDocumentFragment();
+
+ for (var i = 0; i < data.length; i++) {
+ if (data[i].type === 'text') {
+ fragment.appendChild(document.createTextNode(data[i].data));
+ } else {
+ fragment.appendChild(createAccessibleMarkupPair(data[i].data, data[i].mathstyle, options, true));
+ }
+ }
+ }
+
+ return fragment;
+}
+
+function scanElement(elem, options) {
+ var originalContent = elem.getAttribute('data-' + options.namespace + 'original-content');
+
+ if (originalContent) {
+ var mathstyle = elem.getAttribute('data-' + options.namespace + 'mathstyle');
+ var span = createAccessibleMarkupPair(originalContent, mathstyle, options, false);
+
+ if (span != null) {
+ elem.textContent = '';
+ elem.appendChild(span);
+ }
+
+ return;
+ }
+
+ if (elem.childNodes.length === 1 && elem.childNodes[0].nodeType === 3) {
+ // This is a node with textual content only. Perhaps an opportunity
+ // to simplify and avoid creating extra nested elements...
+ var text = elem.childNodes[0].textContent;
+
+ if (options.TeX.processEnvironments && /^\s*\\begin/.test(text)) {
+ elem.textContent = '';
+ elem.appendChild(createAccessibleMarkupPair(text, undefined, options, true));
+ return;
+ }
+
+ var data = splitWithDelimiters(text, options.TeX.delimiters);
+
+ if (data.length === 1 && data[0].type === 'math') {
+ // The entire content is a math expression: we can replace the content
+ // with the latex markup without creating additional wrappers.
+ elem.textContent = '';
+ elem.appendChild(createAccessibleMarkupPair(data[0].data, data[0].mathstyle, options, true));
+ return;
+ } else if (data.length === 1 && data[0].type === 'text') {
+ // This element only contained text with no math. No need to
+ // do anything.
+ return;
+ }
+ }
+
+ for (var i = 0; i < elem.childNodes.length; i++) {
+ var childNode = elem.childNodes[i];
+
+ if (childNode.nodeType === 3) {
+ // A text node
+ // Look for math mode delimiters inside the text
+ var frag = scanText(childNode.textContent, options);
+
+ if (frag) {
+ i += frag.childNodes.length - 1;
+ elem.replaceChild(frag, childNode);
+ }
+ } else if (childNode.nodeType === 1) {
+ // An element node
+ var tag = childNode.nodeName.toLowerCase();
+
+ if (tag === 'script' && options.processScriptTypePattern.test(childNode.type)) {
+ var style = 'displaystyle';
+ var _iteratorNormalCompletion = true;
+ var _didIteratorError = false;
+ var _iteratorError = undefined;
+
+ try {
+ for (var _iterator = childNode.type.split(';')[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
+ var l = _step.value;
+ var v = l.split('=');
+
+ if (v[0].toLowerCase() === 'mode') {
+ if (v[1].toLoweCase() === 'display') {
+ style = 'displaystyle';
+ } else {
+ style = 'textstyle';
+ }
+ }
+ }
+ } catch (err) {
+ _didIteratorError = true;
+ _iteratorError = err;
+ } finally {
+ try {
+ if (!_iteratorNormalCompletion && _iterator.return != null) {
+ _iterator.return();
+ }
+ } finally {
+ if (_didIteratorError) {
+ throw _iteratorError;
+ }
+ }
+ }
+
+ var _span = createAccessibleMarkupPair(childNode.textContent, style, options, true);
+
+ childNode.parentNode.replaceChild(_span, childNode);
+ } else {
+ // Element node
+ var shouldRender = options.processClassPattern.test(childNode.className) || !(options.skipTags.includes(tag) || options.ignoreClassPattern.test(childNode.className));
+
+ if (shouldRender) {
+ scanElement(childNode, options);
+ }
+ }
+ } // Otherwise, it's something else, and ignore it.
+
+ }
+}
+
+var defaultOptions = {
+ // Optional namespace for the `data-` attributes.
+ namespace: '',
+ // Name of tags whose content will not be scanned for math delimiters
+ skipTags: ['noscript', 'style', 'textarea', 'pre', 'code', 'annotation', 'annotation-xml'],
+ //
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
A span is the most elementary element that can be rendered.
-It is composed of an optional body of text and an optional list
-of children (other spans). Each span can be decorated with
-CSS classes and style attributes.
-
-
-
-
-
-
-
-
-
-
-
-
Constructor
-
-
-
new exports.Span(content: string | Span | Span[], classes: string): voidprivate