| // Parser MathBlock |
| var latexMathParser = (function() { |
| function commandToBlock(cmd) { // can also take in a Fragment |
| var block = MathBlock(); |
| cmd.adopt(block, 0, 0); |
| return block; |
| } |
| function joinBlocks(blocks) { |
| var firstBlock = blocks[0] || MathBlock(); |
| |
| for (var i = 1; i < blocks.length; i += 1) { |
| blocks[i].children().adopt(firstBlock, firstBlock.ends[R], 0); |
| } |
| |
| return firstBlock; |
| } |
| |
| var string = Parser.string; |
| var regex = Parser.regex; |
| var letter = Parser.letter; |
| var any = Parser.any; |
| var optWhitespace = Parser.optWhitespace; |
| var succeed = Parser.succeed; |
| var fail = Parser.fail; |
| |
| // Parsers yielding either MathCommands, or Fragments of MathCommands |
| // (either way, something that can be adopted by a MathBlock) |
| var variable = letter.map(function(c) { return Letter(c); }); |
| var symbol = regex(/^[^${}\\_^]/).map(function(c) { return VanillaSymbol(c); }); |
| |
| var controlSequence = |
| regex(/^[^\\a-eg-zA-Z]/) // hotfix #164; match MathBlock::write |
| .or(string('\\').then( |
| regex(/^[a-z]+/i) |
| .or(regex(/^\s+/).result(' ')) |
| .or(any) |
| )).then(function(ctrlSeq) { |
| var cmdKlass = LatexCmds[ctrlSeq]; |
| |
| if (cmdKlass) { |
| return cmdKlass(ctrlSeq).parser(); |
| } |
| else { |
| return fail('unknown command: \\'+ctrlSeq); |
| } |
| }) |
| ; |
| |
| var command = |
| controlSequence |
| .or(variable) |
| .or(symbol) |
| ; |
| |
| // Parsers yielding MathBlocks |
| var mathGroup = string('{').then(function() { return mathSequence; }).skip(string('}')); |
| var mathBlock = optWhitespace.then(mathGroup.or(command.map(commandToBlock))); |
| var mathSequence = mathBlock.many().map(joinBlocks).skip(optWhitespace); |
| |
| var optMathBlock = |
| string('[').then( |
| mathBlock.then(function(block) { |
| return block.join('latex') !== ']' ? succeed(block) : fail(); |
| }) |
| .many().map(joinBlocks).skip(optWhitespace) |
| ).skip(string(']')) |
| ; |
| |
| var latexMath = mathSequence; |
| |
| latexMath.block = mathBlock; |
| latexMath.optBlock = optMathBlock; |
| return latexMath; |
| })(); |
| |
| Controller.open(function(_, super_) { |
| _.exportLatex = function() { |
| return this.root.latex().replace(/(\\[a-z]+) (?![a-z])/ig,'$1'); |
| }; |
| _.writeLatex = function(latex) { |
| var cursor = this.notify('edit').cursor; |
| |
| var all = Parser.all; |
| var eof = Parser.eof; |
| |
| var block = latexMathParser.skip(eof).or(all.result(false)).parse(latex); |
| |
| if (block && !block.isEmpty()) { |
| block.children().adopt(cursor.parent, cursor[L], cursor[R]); |
| var jQ = block.jQize(); |
| jQ.insertBefore(cursor.jQ); |
| cursor[L] = block.ends[R]; |
| block.finalizeInsert(cursor.options, cursor); |
| if (block.ends[R][R].siblingCreated) block.ends[R][R].siblingCreated(cursor.options, L); |
| if (block.ends[L][L].siblingCreated) block.ends[L][L].siblingCreated(cursor.options, R); |
| cursor.parent.bubble('reflow'); |
| } |
| |
| return this; |
| }; |
| _.renderLatexMath = function(latex) { |
| var root = this.root, cursor = this.cursor; |
| |
| var all = Parser.all; |
| var eof = Parser.eof; |
| |
| var block = latexMathParser.skip(eof).or(all.result(false)).parse(latex); |
| |
| root.eachChild('postOrder', 'dispose'); |
| root.ends[L] = root.ends[R] = 0; |
| |
| if (block) { |
| block.children().adopt(root, 0, 0); |
| } |
| |
| var jQ = root.jQ; |
| |
| if (block) { |
| var html = block.join('html'); |
| jQ.html(html); |
| root.jQize(jQ.children()); |
| root.finalizeInsert(cursor.options); |
| } |
| else { |
| jQ.empty(); |
| } |
| |
| delete cursor.selection; |
| cursor.insAtRightEnd(root); |
| }; |
| _.renderLatexText = function(latex) { |
| var root = this.root, cursor = this.cursor; |
| |
| root.jQ.children().slice(1).remove(); |
| root.eachChild('postOrder', 'dispose'); |
| root.ends[L] = root.ends[R] = 0; |
| delete cursor.selection; |
| cursor.show().insAtRightEnd(root); |
| |
| var regex = Parser.regex; |
| var string = Parser.string; |
| var eof = Parser.eof; |
| var all = Parser.all; |
| |
| // Parser RootMathCommand |
| var mathMode = string('$').then(latexMathParser) |
| // because TeX is insane, math mode doesn't necessarily |
| // have to end. So we allow for the case that math mode |
| // continues to the end of the stream. |
| .skip(string('$').or(eof)) |
| .map(function(block) { |
| // HACK FIXME: this shouldn't have to have access to cursor |
| var rootMathCommand = RootMathCommand(cursor); |
| |
| rootMathCommand.createBlocks(); |
| var rootMathBlock = rootMathCommand.ends[L]; |
| block.children().adopt(rootMathBlock, 0, 0); |
| |
| return rootMathCommand; |
| }) |
| ; |
| |
| var escapedDollar = string('\\$').result('$'); |
| var textChar = escapedDollar.or(regex(/^[^$]/)).map(VanillaSymbol); |
| var latexText = mathMode.or(textChar).many(); |
| var commands = latexText.skip(eof).or(all.result(false)).parse(latex); |
| |
| if (commands) { |
| for (var i = 0; i < commands.length; i += 1) { |
| commands[i].adopt(root, root.ends[R], 0); |
| } |
| |
| root.jQize().appendTo(root.jQ); |
| |
| root.finalizeInsert(cursor.options); |
| } |
| }; |
| }); |