org.js (59739B)
1 // Generated by export.rb at Fri Apr 26 15:29:06 UTC 2019 2 /* 3 Copyright (c) 2014 Masafumi Oyamada 4 5 Permission is hereby granted, free of charge, to any person obtaining a copy 6 of this software and associated documentation files (the "Software"), to deal 7 in the Software without restriction, including without limitation the rights 8 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 copies of the Software, and to permit persons to whom the Software is 10 furnished to do so, subject to the following conditions: 11 12 The above copyright notice and this permission notice shall be included in 13 all copies or substantial portions of the Software. 14 15 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 21 THE SOFTWARE. 22 */ 23 var Org = (function () { 24 var exports = {}; 25 26 // ------------------------------------------------------------ 27 // Syntax 28 // ------------------------------------------------------------ 29 30 var Syntax = { 31 rules: {}, 32 33 define: function (name, syntax) { 34 this.rules[name] = syntax; 35 var methodName = "is" + name.substring(0, 1).toUpperCase() + name.substring(1); 36 this[methodName] = function (line) { 37 return this.rules[name].exec(line); 38 }; 39 } 40 }; 41 42 Syntax.define("header", /^(\*+)\s+(.*)$/); // m[1] => level, m[2] => content 43 Syntax.define("preformatted", /^(\s*):(?: (.*)$|$)/); // m[1] => indentation, m[2] => content 44 Syntax.define("unorderedListElement", /^(\s*)(?:-|\+|\s+\*)\s+(.*)$/); // m[1] => indentation, m[2] => content 45 Syntax.define("orderedListElement", /^(\s*)(\d+)(?:\.|\))\s+(.*)$/); // m[1] => indentation, m[2] => number, m[3] => content 46 Syntax.define("tableSeparator", /^(\s*)\|((?:\+|-)*?)\|?$/); // m[1] => indentation, m[2] => content 47 Syntax.define("tableRow", /^(\s*)\|(.*?)\|?$/); // m[1] => indentation, m[2] => content 48 Syntax.define("blank", /^$/); 49 Syntax.define("horizontalRule", /^(\s*)-{5,}$/); // 50 Syntax.define("directive", /^(\s*)#\+(?:(begin|end)_)?(.*)$/i); // m[1] => indentation, m[2] => type, m[3] => content 51 Syntax.define("comment", /^(\s*)#(.*)$/); 52 Syntax.define("line", /^(\s*)(.*)$/); 53 Syntax.define("property", /^([A-Z]+):\s(.*)$/); // m[1] => keyword, m[2] => value 54 55 // ------------------------------------------------------------ 56 // Token 57 // ------------------------------------------------------------ 58 59 function Token() { 60 } 61 62 Token.prototype = { 63 isListElement: function () { 64 return this.type === Lexer.tokens.orderedListElement || 65 this.type === Lexer.tokens.unorderedListElement; 66 }, 67 68 isTableElement: function () { 69 return this.type === Lexer.tokens.tableSeparator || 70 this.type === Lexer.tokens.tableRow; 71 } 72 }; 73 74 // ------------------------------------------------------------ 75 // Lexer 76 // ------------------------------------------------------------ 77 78 function Lexer(stream) { 79 this.stream = stream; 80 this.tokenStack = []; 81 } 82 83 Lexer.prototype = { 84 tokenize: function (line) { 85 var token = new Token(); 86 token.fromLineNumber = this.stream.lineNumber; 87 88 if (Syntax.isHeader(line)) { 89 token.type = Lexer.tokens.header; 90 token.indentation = 0; 91 token.content = RegExp.$2; 92 // specific 93 token.level = RegExp.$1.length; 94 } else if(Syntax.isProperty(line)) { 95 token.type = Lexer.tokens.property; 96 token.indentation = 0; 97 token.content = RegExp.$2; 98 // specific 99 token.keyword = RegExp.$1; 100 } else if (Syntax.isPreformatted(line)) { 101 token.type = Lexer.tokens.preformatted; 102 token.indentation = RegExp.$1.length; 103 token.content = RegExp.$2; 104 } else if (Syntax.isUnorderedListElement(line)) { 105 token.type = Lexer.tokens.unorderedListElement; 106 token.indentation = RegExp.$1.length; 107 token.content = RegExp.$2; 108 } else if (Syntax.isOrderedListElement(line)) { 109 token.type = Lexer.tokens.orderedListElement; 110 token.indentation = RegExp.$1.length; 111 token.content = RegExp.$3; 112 // specific 113 token.number = RegExp.$2; 114 } else if (Syntax.isTableSeparator(line)) { 115 token.type = Lexer.tokens.tableSeparator; 116 token.indentation = RegExp.$1.length; 117 token.content = RegExp.$2; 118 } else if (Syntax.isTableRow(line)) { 119 token.type = Lexer.tokens.tableRow; 120 token.indentation = RegExp.$1.length; 121 token.content = RegExp.$2; 122 } else if (Syntax.isBlank(line)) { 123 token.type = Lexer.tokens.blank; 124 token.indentation = 0; 125 token.content = null; 126 } else if (Syntax.isHorizontalRule(line)) { 127 token.type = Lexer.tokens.horizontalRule; 128 token.indentation = RegExp.$1.length; 129 token.content = null; 130 } else if (Syntax.isDirective(line)) { 131 token.type = Lexer.tokens.directive; 132 token.indentation = RegExp.$1.length; 133 token.content = RegExp.$3; 134 // decide directive type (begin, end or oneshot) 135 var directiveTypeString = RegExp.$2; 136 if (/^begin/i.test(directiveTypeString)) 137 token.beginDirective = true; 138 else if (/^end/i.test(directiveTypeString)) 139 token.endDirective = true; 140 else 141 token.oneshotDirective = true; 142 } else if (Syntax.isComment(line)) { 143 token.type = Lexer.tokens.comment; 144 token.indentation = RegExp.$1.length; 145 token.content = RegExp.$2; 146 } else if (Syntax.isLine(line)) { 147 token.type = Lexer.tokens.line; 148 token.indentation = RegExp.$1.length; 149 token.content = RegExp.$2; 150 } else { 151 throw new Error("SyntaxError: Unknown line: " + line); 152 } 153 154 return token; 155 }, 156 157 pushToken: function (token) { 158 this.tokenStack.push(token); 159 }, 160 161 pushDummyTokenByType: function (type) { 162 var token = new Token(); 163 token.type = type; 164 this.tokenStack.push(token); 165 }, 166 167 peekStackedToken: function () { 168 return this.tokenStack.length > 0 ? 169 this.tokenStack[this.tokenStack.length - 1] : null; 170 }, 171 172 getStackedToken: function () { 173 return this.tokenStack.length > 0 ? 174 this.tokenStack.pop() : null; 175 }, 176 177 peekNextToken: function () { 178 return this.peekStackedToken() || 179 this.tokenize(this.stream.peekNextLine()); 180 }, 181 182 getNextToken: function () { 183 return this.getStackedToken() || 184 this.tokenize(this.stream.getNextLine()); 185 }, 186 187 hasNext: function () { 188 return this.stream.hasNext(); 189 }, 190 191 getLineNumber: function () { 192 return this.stream.lineNumber; 193 } 194 }; 195 196 Lexer.tokens = {}; 197 [ 198 "header", 199 "orderedListElement", 200 "unorderedListElement", 201 "tableRow", 202 "tableSeparator", 203 "preformatted", 204 "line", 205 "horizontalRule", 206 "blank", 207 "directive", 208 "comment", 209 "property" 210 ].forEach(function (tokenName, i) { 211 Lexer.tokens[tokenName] = i; 212 }); 213 214 // ------------------------------------------------------------ 215 // Exports 216 // ------------------------------------------------------------ 217 218 if (typeof exports !== "undefined") 219 exports.Lexer = Lexer; 220 221 function PrototypeNode(type, children) { 222 this.type = type; 223 this.children = []; 224 225 if (children) { 226 for (var i = 0, len = children.length; i < len; ++i) { 227 this.appendChild(children[i]); 228 } 229 } 230 } 231 PrototypeNode.prototype = { 232 previousSibling: null, 233 parent: null, 234 get firstChild() { 235 return this.children.length < 1 ? 236 null : this.children[0]; 237 }, 238 get lastChild() { 239 return this.children.length < 1 ? 240 null : this.children[this.children.length - 1]; 241 }, 242 appendChild: function (newChild) { 243 var previousSibling = this.children.length < 1 ? 244 null : this.lastChild; 245 this.children.push(newChild); 246 newChild.previousSibling = previousSibling; 247 newChild.parent = this; 248 }, 249 toString: function () { 250 var string = "<" + this.type + ">"; 251 252 if (typeof this.value !== "undefined") { 253 string += " " + this.value; 254 } else if (this.children) { 255 string += "\n" + this.children.map(function (child, idx) { 256 return "#" + idx + " " + child.toString(); 257 }).join("\n").split("\n").map(function (line) { 258 return " " + line; 259 }).join("\n"); 260 } 261 262 return string; 263 } 264 }; 265 266 var Node = { 267 types: {}, 268 269 define: function (name, postProcess) { 270 this.types[name] = name; 271 272 var methodName = "create" + name.substring(0, 1).toUpperCase() + name.substring(1); 273 var postProcessGiven = typeof postProcess === "function"; 274 275 this[methodName] = function (children, options) { 276 var node = new PrototypeNode(name, children); 277 278 if (postProcessGiven) 279 postProcess(node, options || {}); 280 281 return node; 282 }; 283 } 284 }; 285 286 Node.define("text", function (node, options) { 287 node.value = options.value; 288 }); 289 Node.define("header", function (node, options) { 290 node.level = options.level; 291 }); 292 Node.define("property"); 293 Node.define("orderedList"); 294 Node.define("unorderedList"); 295 Node.define("definitionList"); 296 Node.define("listElement"); 297 Node.define("paragraph"); 298 Node.define("preformatted"); 299 Node.define("table"); 300 Node.define("tableRow"); 301 Node.define("tableCell"); 302 Node.define("horizontalRule"); 303 Node.define("directive"); 304 305 // Inline 306 Node.define("inlineContainer"); 307 308 Node.define("bold"); 309 Node.define("italic"); 310 Node.define("underline"); 311 Node.define("code"); 312 Node.define("verbatim"); 313 Node.define("dashed"); 314 Node.define("link", function (node, options) { 315 node.src = options.src; 316 }); 317 318 if (typeof exports !== "undefined") 319 exports.Node = Node; 320 321 function Stream(sequence) { 322 this.sequences = sequence.split(/\r?\n/); 323 this.totalLines = this.sequences.length; 324 this.lineNumber = 0; 325 } 326 327 Stream.prototype.peekNextLine = function () { 328 return this.hasNext() ? this.sequences[this.lineNumber] : null; 329 }; 330 331 Stream.prototype.getNextLine = function () { 332 return this.hasNext() ? this.sequences[this.lineNumber++] : null; 333 }; 334 335 Stream.prototype.hasNext = function () { 336 return this.lineNumber < this.totalLines; 337 }; 338 339 if (typeof exports !== "undefined") { 340 exports.Stream = Stream; 341 } 342 343 // var Stream = require("./stream.js").Stream; 344 // var Lexer = require("./lexer.js").Lexer; 345 // var Node = require("./node.js").Node; 346 347 function Parser() { 348 this.inlineParser = new InlineParser(); 349 } 350 351 Parser.parseStream = function (stream, options) { 352 var parser = new Parser(); 353 parser.initStatus(stream, options); 354 parser.parseNodes(); 355 return parser.nodes; 356 }; 357 358 Parser.prototype = { 359 initStatus: function (stream, options) { 360 if (typeof stream === "string") 361 stream = new Stream(stream); 362 this.lexer = new Lexer(stream); 363 this.nodes = []; 364 this.options = { 365 toc: true, 366 num: true, 367 "^": "{}", 368 multilineCell: false 369 }; 370 // Override option values 371 if (options && typeof options === "object") { 372 for (var key in options) { 373 this.options[key] = options[key]; 374 } 375 } 376 this.document = { 377 options: this.options, 378 directiveValues: {}, 379 convert: function (ConverterClass, exportOptions) { 380 var converter = new ConverterClass(this, exportOptions); 381 return converter.result; 382 } 383 }; 384 }, 385 386 parse: function (stream, options) { 387 this.initStatus(stream, options); 388 this.parseDocument(); 389 this.document.nodes = this.nodes; 390 return this.document; 391 }, 392 393 createErrorReport: function (message) { 394 return new Error(message + " at line " + this.lexer.getLineNumber()); 395 }, 396 397 skipBlank: function () { 398 var blankToken = null; 399 while (this.lexer.peekNextToken().type === Lexer.tokens.blank) 400 blankToken = this.lexer.getNextToken(); 401 return blankToken; 402 }, 403 404 setNodeOriginFromToken: function (node, token) { 405 node.fromLineNumber = token.fromLineNumber; 406 return node; 407 }, 408 409 appendNode: function (newNode) { 410 var previousSibling = this.nodes.length > 0 ? this.nodes[this.nodes.length - 1] : null; 411 this.nodes.push(newNode); 412 newNode.previousSibling = previousSibling; 413 }, 414 415 // ------------------------------------------------------------ 416 // <Document> ::= <Element>* 417 // ------------------------------------------------------------ 418 419 parseDocument: function () { 420 this.parseTitle(); 421 this.parseNodes(); 422 }, 423 424 parseNodes: function () { 425 while (this.lexer.hasNext()) { 426 var element = this.parseElement(); 427 if (element) this.appendNode(element); 428 } 429 }, 430 431 parseTitle: function () { 432 this.skipBlank(); 433 434 if (this.lexer.hasNext() && 435 this.lexer.peekNextToken().type === Lexer.tokens.line) 436 this.document.title = this.createTextNode(this.lexer.getNextToken().content); 437 else 438 this.document.title = null; 439 440 this.lexer.pushDummyTokenByType(Lexer.tokens.blank); 441 }, 442 443 // ------------------------------------------------------------ 444 // <Element> ::= (<Header> | <List> 445 // | <Preformatted> | <Paragraph> 446 // | <Table>)* 447 // ------------------------------------------------------------ 448 449 parseElement: function () { 450 var element = null; 451 452 switch (this.lexer.peekNextToken().type) { 453 case Lexer.tokens.header: 454 element = this.parseHeader(); 455 break; 456 case Lexer.tokens.property: 457 element = this.parseProperty(); 458 break; 459 case Lexer.tokens.preformatted: 460 element = this.parsePreformatted(); 461 break; 462 case Lexer.tokens.orderedListElement: 463 case Lexer.tokens.unorderedListElement: 464 element = this.parseList(); 465 break; 466 case Lexer.tokens.line: 467 element = this.parseText(); 468 break; 469 case Lexer.tokens.tableRow: 470 case Lexer.tokens.tableSeparator: 471 element = this.parseTable(); 472 break; 473 case Lexer.tokens.blank: 474 this.skipBlank(); 475 if (this.lexer.hasNext()) { 476 if (this.lexer.peekNextToken().type === Lexer.tokens.line) 477 element = this.parseParagraph(); 478 else 479 element = this.parseElement(); 480 } 481 break; 482 case Lexer.tokens.horizontalRule: 483 this.lexer.getNextToken(); 484 element = Node.createHorizontalRule(); 485 break; 486 case Lexer.tokens.directive: 487 element = this.parseDirective(); 488 break; 489 case Lexer.tokens.comment: 490 // Skip 491 this.lexer.getNextToken(); 492 break; 493 default: 494 throw this.createErrorReport("Unhandled token: " + this.lexer.peekNextToken().type); 495 } 496 497 return element; 498 }, 499 500 parseElementBesidesDirectiveEnd: function () { 501 try { 502 // Temporary, override the definition of `parseElement` 503 this.parseElement = this.parseElementBesidesDirectiveEndBody; 504 return this.parseElement(); 505 } finally { 506 this.parseElement = this.originalParseElement; 507 } 508 }, 509 510 parseElementBesidesDirectiveEndBody: function () { 511 if (this.lexer.peekNextToken().type === Lexer.tokens.directive && 512 this.lexer.peekNextToken().endDirective) { 513 return null; 514 } 515 516 return this.originalParseElement(); 517 }, 518 519 // ------------------------------------------------------------ 520 // <Header> 521 // 522 // : preformatted 523 // : block 524 // ------------------------------------------------------------ 525 526 parseHeader: function () { 527 var headerToken = this.lexer.getNextToken(); 528 var header = Node.createHeader([ 529 this.createTextNode(headerToken.content) // TODO: Parse inline markups 530 ], { level: headerToken.level }); 531 this.setNodeOriginFromToken(header, headerToken); 532 533 while(this.lexer.hasNext() && this.lexer.peekNextToken().type === Lexer.tokens.property){ 534 var property = this.parseProperty(); 535 header.appendChild(property); 536 } 537 538 return header; 539 }, 540 541 parseProperty: function(rootToken){ 542 var propertyToken = this.lexer.getNextToken(); 543 var property = Node.createProperty(null); 544 this.setNodeOriginFromToken(property, propertyToken); 545 property.keyword = propertyToken.keyword; 546 property.content = propertyToken.content; 547 return property; 548 }, 549 550 // ------------------------------------------------------------ 551 // <Preformatted> 552 // 553 // : preformatted 554 // : block 555 // ------------------------------------------------------------ 556 557 parsePreformatted: function () { 558 var preformattedFirstToken = this.lexer.peekNextToken(); 559 var preformatted = Node.createPreformatted([]); 560 this.setNodeOriginFromToken(preformatted, preformattedFirstToken); 561 562 var textContents = []; 563 564 while (this.lexer.hasNext()) { 565 var token = this.lexer.peekNextToken(); 566 if (token.type !== Lexer.tokens.preformatted || 567 token.indentation < preformattedFirstToken.indentation) 568 break; 569 this.lexer.getNextToken(); 570 textContents.push(token.content); 571 } 572 573 preformatted.appendChild(this.createTextNode(textContents.join("\n"), true /* no emphasis */)); 574 575 return preformatted; 576 }, 577 578 // ------------------------------------------------------------ 579 // <List> 580 // 581 // - foo 582 // 1. bar 583 // 2. baz 584 // ------------------------------------------------------------ 585 586 // XXX: not consider codes (e.g., =Foo::Bar=) 587 definitionPattern: /^(.*?) :: *(.*)$/, 588 589 parseList: function () { 590 var rootToken = this.lexer.peekNextToken(); 591 var list; 592 var isDefinitionList = false; 593 594 if (this.definitionPattern.test(rootToken.content)) { 595 list = Node.createDefinitionList([]); 596 isDefinitionList = true; 597 } else { 598 list = rootToken.type === Lexer.tokens.unorderedListElement ? 599 Node.createUnorderedList([]) : Node.createOrderedList([]); 600 } 601 this.setNodeOriginFromToken(list, rootToken); 602 603 while (this.lexer.hasNext()) { 604 var nextToken = this.lexer.peekNextToken(); 605 if (!nextToken.isListElement() || nextToken.indentation !== rootToken.indentation) 606 break; 607 list.appendChild(this.parseListElement(rootToken.indentation, isDefinitionList)); 608 } 609 610 return list; 611 }, 612 613 unknownDefinitionTerm: "???", 614 615 parseListElement: function (rootIndentation, isDefinitionList) { 616 var listElementToken = this.lexer.getNextToken(); 617 var listElement = Node.createListElement([]); 618 this.setNodeOriginFromToken(listElement, listElementToken); 619 620 listElement.isDefinitionList = isDefinitionList; 621 622 if (isDefinitionList) { 623 var match = this.definitionPattern.exec(listElementToken.content); 624 listElement.term = [ 625 this.createTextNode(match && match[1] ? match[1] : this.unknownDefinitionTerm) 626 ]; 627 listElement.appendChild(this.createTextNode(match ? match[2] : listElementToken.content)); 628 } else { 629 listElement.appendChild(this.createTextNode(listElementToken.content)); 630 } 631 632 while (this.lexer.hasNext()) { 633 var blankToken = this.skipBlank(); 634 if (!this.lexer.hasNext()) 635 break; 636 637 var notBlankNextToken = this.lexer.peekNextToken(); 638 if (blankToken && !notBlankNextToken.isListElement()) 639 this.lexer.pushToken(blankToken); // Recover blank token only when next line is not listElement. 640 if (notBlankNextToken.indentation <= rootIndentation) 641 break; // end of the list 642 643 var element = this.parseElement(); // recursive 644 if (element) 645 listElement.appendChild(element); 646 } 647 648 return listElement; 649 }, 650 651 // ------------------------------------------------------------ 652 // <Table> ::= <TableRow>+ 653 // ------------------------------------------------------------ 654 655 parseTable: function () { 656 var nextToken = this.lexer.peekNextToken(); 657 var table = Node.createTable([]); 658 this.setNodeOriginFromToken(table, nextToken); 659 var sawSeparator = false; 660 661 var allowMultilineCell = nextToken.type === Lexer.tokens.tableSeparator && this.options.multilineCell; 662 663 while (this.lexer.hasNext() && 664 (nextToken = this.lexer.peekNextToken()).isTableElement()) { 665 if (nextToken.type === Lexer.tokens.tableRow) { 666 var tableRow = this.parseTableRow(allowMultilineCell); 667 table.appendChild(tableRow); 668 } else { 669 // Lexer.tokens.tableSeparator 670 sawSeparator = true; 671 this.lexer.getNextToken(); 672 } 673 } 674 675 if (sawSeparator && table.children.length) { 676 table.children[0].children.forEach(function (cell) { 677 cell.isHeader = true; 678 }); 679 } 680 681 return table; 682 }, 683 684 // ------------------------------------------------------------ 685 // <TableRow> ::= <TableCell>+ 686 // ------------------------------------------------------------ 687 688 parseTableRow: function (allowMultilineCell) { 689 var tableRowTokens = []; 690 691 while (this.lexer.peekNextToken().type === Lexer.tokens.tableRow) { 692 tableRowTokens.push(this.lexer.getNextToken()); 693 if (!allowMultilineCell) { 694 break; 695 } 696 } 697 698 if (!tableRowTokens.length) { 699 throw this.createErrorReport("Expected table row"); 700 } 701 702 var firstTableRowToken = tableRowTokens.shift(); 703 var tableCellTexts = firstTableRowToken.content.split("|"); 704 705 tableRowTokens.forEach(function (rowToken) { 706 rowToken.content.split("|").forEach(function (cellText, cellIdx) { 707 tableCellTexts[cellIdx] = (tableCellTexts[cellIdx] || "") + "\n" + cellText; 708 }); 709 }); 710 711 // TODO: Prepare two pathes: (1) 712 var tableCells = tableCellTexts.map( 713 // TODO: consider '|' escape? 714 function (text) { 715 return Node.createTableCell(Parser.parseStream(text)); 716 }, this); 717 718 return this.setNodeOriginFromToken(Node.createTableRow(tableCells), firstTableRowToken); 719 }, 720 721 // ------------------------------------------------------------ 722 // <Directive> ::= "#+.*" 723 // ------------------------------------------------------------ 724 725 parseDirective: function () { 726 var directiveToken = this.lexer.getNextToken(); 727 var directiveNode = this.createDirectiveNodeFromToken(directiveToken); 728 729 if (directiveToken.endDirective) 730 throw this.createErrorReport("Unmatched 'end' directive for " + directiveNode.directiveName); 731 732 if (directiveToken.oneshotDirective) { 733 this.interpretDirective(directiveNode); 734 return directiveNode; 735 } 736 737 if (!directiveToken.beginDirective) 738 throw this.createErrorReport("Invalid directive " + directiveNode.directiveName); 739 740 // Parse begin ~ end 741 directiveNode.children = []; 742 if (this.isVerbatimDirective(directiveNode)) 743 return this.parseDirectiveBlockVerbatim(directiveNode); 744 else 745 return this.parseDirectiveBlock(directiveNode); 746 }, 747 748 createDirectiveNodeFromToken: function (directiveToken) { 749 var matched = /^[ ]*([^ ]*)[ ]*(.*)[ ]*$/.exec(directiveToken.content); 750 751 var directiveNode = Node.createDirective(null); 752 this.setNodeOriginFromToken(directiveNode, directiveToken); 753 directiveNode.directiveName = matched[1].toLowerCase(); 754 directiveNode.directiveArguments = this.parseDirectiveArguments(matched[2]); 755 directiveNode.directiveOptions = this.parseDirectiveOptions(matched[2]); 756 directiveNode.directiveRawValue = matched[2]; 757 758 return directiveNode; 759 }, 760 761 isVerbatimDirective: function (directiveNode) { 762 var directiveName = directiveNode.directiveName; 763 return directiveName === "src" || directiveName === "example" || directiveName === "html"; 764 }, 765 766 parseDirectiveBlock: function (directiveNode, verbatim) { 767 this.lexer.pushDummyTokenByType(Lexer.tokens.blank); 768 769 while (this.lexer.hasNext()) { 770 var nextToken = this.lexer.peekNextToken(); 771 if (nextToken.type === Lexer.tokens.directive && 772 nextToken.endDirective && 773 this.createDirectiveNodeFromToken(nextToken).directiveName === directiveNode.directiveName) { 774 // Close directive 775 this.lexer.getNextToken(); 776 return directiveNode; 777 } 778 var element = this.parseElementBesidesDirectiveEnd(); 779 if (element) 780 directiveNode.appendChild(element); 781 } 782 783 throw this.createErrorReport("Unclosed directive " + directiveNode.directiveName); 784 }, 785 786 parseDirectiveBlockVerbatim: function (directiveNode) { 787 var textContent = []; 788 789 while (this.lexer.hasNext()) { 790 var nextToken = this.lexer.peekNextToken(); 791 if (nextToken.type === Lexer.tokens.directive && 792 nextToken.endDirective && 793 this.createDirectiveNodeFromToken(nextToken).directiveName === directiveNode.directiveName) { 794 this.lexer.getNextToken(); 795 directiveNode.appendChild(this.createTextNode(textContent.join("\n"), true)); 796 return directiveNode; 797 } 798 textContent.push(this.lexer.stream.getNextLine()); 799 } 800 801 throw this.createErrorReport("Unclosed directive " + directiveNode.directiveName); 802 }, 803 804 parseDirectiveArguments: function (parameters) { 805 return parameters.split(/[ ]+/).filter(function (param) { 806 return param.length && param[0] !== "-"; 807 }); 808 }, 809 810 parseDirectiveOptions: function (parameters) { 811 return parameters.split(/[ ]+/).filter(function (param) { 812 return param.length && param[0] === "-"; 813 }); 814 }, 815 816 interpretDirective: function (directiveNode) { 817 // http://orgmode.org/manual/Export-options.html 818 switch (directiveNode.directiveName) { 819 case "options:": 820 this.interpretOptionDirective(directiveNode); 821 break; 822 case "title:": 823 this.document.title = directiveNode.directiveRawValue; 824 break; 825 case "author:": 826 this.document.author = directiveNode.directiveRawValue; 827 break; 828 case "email:": 829 this.document.email = directiveNode.directiveRawValue; 830 break; 831 default: 832 this.document.directiveValues[directiveNode.directiveName] = directiveNode.directiveRawValue; 833 break; 834 } 835 }, 836 837 interpretOptionDirective: function (optionDirectiveNode) { 838 optionDirectiveNode.directiveArguments.forEach(function (pairString) { 839 var pair = pairString.split(":"); 840 this.options[pair[0]] = this.convertLispyValue(pair[1]); 841 }, this); 842 }, 843 844 convertLispyValue: function (lispyValue) { 845 switch (lispyValue) { 846 case "t": 847 return true; 848 case "nil": 849 return false; 850 default: 851 if (/^[0-9]+$/.test(lispyValue)) 852 return parseInt(lispyValue); 853 return lispyValue; 854 } 855 }, 856 857 // ------------------------------------------------------------ 858 // <Paragraph> ::= <Blank> <Line>* 859 // ------------------------------------------------------------ 860 861 parseParagraph: function () { 862 var paragraphFisrtToken = this.lexer.peekNextToken(); 863 var paragraph = Node.createParagraph([]); 864 this.setNodeOriginFromToken(paragraph, paragraphFisrtToken); 865 866 var textContents = []; 867 868 while (this.lexer.hasNext()) { 869 var nextToken = this.lexer.peekNextToken(); 870 if (nextToken.type !== Lexer.tokens.line 871 || nextToken.indentation < paragraphFisrtToken.indentation) 872 break; 873 this.lexer.getNextToken(); 874 textContents.push(nextToken.content); 875 } 876 877 paragraph.appendChild(this.createTextNode(textContents.join("\n"))); 878 879 return paragraph; 880 }, 881 882 parseText: function (noEmphasis) { 883 var lineToken = this.lexer.getNextToken(); 884 return this.createTextNode(lineToken.content, noEmphasis); 885 }, 886 887 // ------------------------------------------------------------ 888 // <Text> (DOM Like) 889 // ------------------------------------------------------------ 890 891 createTextNode: function (text, noEmphasis) { 892 return noEmphasis ? Node.createText(null, { value: text }) 893 : this.inlineParser.parseEmphasis(text); 894 } 895 }; 896 Parser.prototype.originalParseElement = Parser.prototype.parseElement; 897 898 // ------------------------------------------------------------ 899 // Parser for Inline Elements 900 // 901 // @refs org-emphasis-regexp-components 902 // ------------------------------------------------------------ 903 904 function InlineParser() { 905 this.preEmphasis = " \t\\('\""; 906 this.postEmphasis = "- \t.,:!?;'\"\\)"; 907 this.borderForbidden = " \t\r\n,\"'"; 908 this.bodyRegexp = "[\\s\\S]*?"; 909 this.markers = "*/_=~+"; 910 911 this.emphasisPattern = this.buildEmphasisPattern(); 912 this.linkPattern = /\[\[([^\]]*)\](?:\[([^\]]*)\])?\]/g; // \1 => link, \2 => text 913 } 914 915 InlineParser.prototype = { 916 parseEmphasis: function (text) { 917 var emphasisPattern = this.emphasisPattern; 918 emphasisPattern.lastIndex = 0; 919 920 var result = [], 921 match, 922 previousLast = 0, 923 savedLastIndex; 924 925 while ((match = emphasisPattern.exec(text))) { 926 var whole = match[0]; 927 var pre = match[1]; 928 var marker = match[2]; 929 var body = match[3]; 930 var post = match[4]; 931 932 { 933 // parse links 934 var matchBegin = emphasisPattern.lastIndex - whole.length; 935 var beforeContent = text.substring(previousLast, matchBegin + pre.length); 936 savedLastIndex = emphasisPattern.lastIndex; 937 result.push(this.parseLink(beforeContent)); 938 emphasisPattern.lastIndex = savedLastIndex; 939 } 940 941 var bodyNode = [Node.createText(null, { value: body })]; 942 var bodyContainer = this.emphasizeElementByMarker(bodyNode, marker); 943 result.push(bodyContainer); 944 945 previousLast = emphasisPattern.lastIndex - post.length; 946 } 947 948 if (emphasisPattern.lastIndex === 0 || 949 emphasisPattern.lastIndex !== text.length - 1) 950 result.push(this.parseLink(text.substring(previousLast))); 951 952 if (result.length === 1) { 953 // Avoid duplicated inline container wrapping 954 return result[0]; 955 } else { 956 return Node.createInlineContainer(result); 957 } 958 }, 959 960 depth: 0, 961 parseLink: function (text) { 962 var linkPattern = this.linkPattern; 963 linkPattern.lastIndex = 0; 964 965 var match, 966 result = [], 967 previousLast = 0, 968 savedLastIndex; 969 970 while ((match = linkPattern.exec(text))) { 971 var whole = match[0]; 972 var src = match[1]; 973 var title = match[2]; 974 975 // parse before content 976 var matchBegin = linkPattern.lastIndex - whole.length; 977 var beforeContent = text.substring(previousLast, matchBegin); 978 result.push(Node.createText(null, { value: beforeContent })); 979 980 // parse link 981 var link = Node.createLink([]); 982 link.src = src; 983 if (title) { 984 savedLastIndex = linkPattern.lastIndex; 985 link.appendChild(this.parseEmphasis(title)); 986 linkPattern.lastIndex = savedLastIndex; 987 } else { 988 link.appendChild(Node.createText(null, { value: src })); 989 } 990 result.push(link); 991 992 previousLast = linkPattern.lastIndex; 993 } 994 995 if (linkPattern.lastIndex === 0 || 996 linkPattern.lastIndex !== text.length - 1) 997 result.push(Node.createText(null, { value: text.substring(previousLast) })); 998 999 return Node.createInlineContainer(result); 1000 }, 1001 1002 emphasizeElementByMarker: function (element, marker) { 1003 switch (marker) { 1004 case "*": 1005 return Node.createBold(element); 1006 case "/": 1007 return Node.createItalic(element); 1008 case "_": 1009 return Node.createUnderline(element); 1010 case "=": 1011 case "~": 1012 return Node.createCode(element); 1013 case "+": 1014 return Node.createDashed(element); 1015 } 1016 }, 1017 1018 buildEmphasisPattern: function () { 1019 return new RegExp( 1020 "([" + this.preEmphasis + "]|^|\r?\n)" + // \1 => pre 1021 "([" + this.markers + "])" + // \2 => marker 1022 "([^" + this.borderForbidden + "]|" + // \3 => body 1023 "[^" + this.borderForbidden + "]" + 1024 this.bodyRegexp + 1025 "[^" + this.borderForbidden + "])" + 1026 "\\2" + 1027 "([" + this.postEmphasis +"]|$|\r?\n)", // \4 => post 1028 // flags 1029 "g" 1030 ); 1031 } 1032 }; 1033 1034 if (typeof exports !== "undefined") { 1035 exports.Parser = Parser; 1036 exports.InlineParser = InlineParser; 1037 } 1038 1039 // var Node = require("../node.js").Node; 1040 1041 function Converter() { 1042 } 1043 1044 Converter.prototype = { 1045 exportOptions: { 1046 headerOffset: 1, 1047 exportFromLineNumber: false, 1048 suppressSubScriptHandling: false, 1049 suppressAutoLink: false, 1050 // HTML 1051 translateSymbolArrow: false, 1052 suppressCheckboxHandling: false, 1053 // { "directive:": function (node, childText, auxData) {} } 1054 customDirectiveHandler: null, 1055 // e.g., "org-js-" 1056 htmlClassPrefix: null, 1057 htmlIdPrefix: null 1058 }, 1059 1060 untitled: "Untitled", 1061 result: null, 1062 1063 // TODO: Manage TODO lists 1064 1065 initialize: function (orgDocument, exportOptions) { 1066 this.orgDocument = orgDocument; 1067 this.documentOptions = orgDocument.options || {}; 1068 this.exportOptions = exportOptions || {}; 1069 1070 this.headers = []; 1071 this.headerOffset = 1072 typeof this.exportOptions.headerOffset === "number" ? this.exportOptions.headerOffset : 1; 1073 this.sectionNumbers = [0]; 1074 }, 1075 1076 createTocItem: function (headerNode, parentTocs) { 1077 var childTocs = []; 1078 childTocs.parent = parentTocs; 1079 var tocItem = { headerNode: headerNode, childTocs: childTocs }; 1080 return tocItem; 1081 }, 1082 1083 computeToc: function (exportTocLevel) { 1084 if (typeof exportTocLevel !== "number") 1085 exportTocLevel = Infinity; 1086 1087 var toc = []; 1088 toc.parent = null; 1089 1090 var previousLevel = 1; 1091 var currentTocs = toc; // first 1092 1093 for (var i = 0; i < this.headers.length; ++i) { 1094 var headerNode = this.headers[i]; 1095 1096 if (headerNode.level > exportTocLevel) 1097 continue; 1098 1099 var levelDiff = headerNode.level - previousLevel; 1100 if (levelDiff > 0) { 1101 for (var j = 0; j < levelDiff; ++j) { 1102 if (currentTocs.length === 0) { 1103 // Create a dummy tocItem 1104 var dummyHeader = Node.createHeader([], { 1105 level: previousLevel + j 1106 }); 1107 dummyHeader.sectionNumberText = ""; 1108 currentTocs.push(this.createTocItem(dummyHeader, currentTocs)); 1109 } 1110 currentTocs = currentTocs[currentTocs.length - 1].childTocs; 1111 } 1112 } else if (levelDiff < 0) { 1113 levelDiff = -levelDiff; 1114 for (var k = 0; k < levelDiff; ++k) { 1115 currentTocs = currentTocs.parent; 1116 } 1117 } 1118 1119 currentTocs.push(this.createTocItem(headerNode, currentTocs)); 1120 1121 previousLevel = headerNode.level; 1122 } 1123 1124 return toc; 1125 }, 1126 1127 convertNode: function (node, recordHeader, insideCodeElement) { 1128 if (!insideCodeElement) { 1129 if (node.type === Node.types.directive) { 1130 if (node.directiveName === "example" || 1131 node.directiveName === "src") { 1132 insideCodeElement = true; 1133 } 1134 } else if (node.type === Node.types.preformatted) { 1135 insideCodeElement = true; 1136 } 1137 } 1138 1139 if (typeof node === "string") { 1140 node = Node.createText(null, { value: node }); 1141 } 1142 1143 var childText = node.children ? this.convertNodesInternal(node.children, recordHeader, insideCodeElement) : ""; 1144 var text; 1145 1146 var auxData = this.computeAuxDataForNode(node); 1147 1148 switch (node.type) { 1149 case Node.types.header: 1150 // Parse task status 1151 var taskStatus = null; 1152 var _match = childText.match(/^[A-Z]{3,}\s+/); 1153 if(_match && _match[0]){ 1154 taskStatus = _match[0].trim().toLowerCase(); 1155 } 1156 1157 // Compute section number 1158 var sectionNumberText = null; 1159 if (recordHeader) { 1160 var thisHeaderLevel = node.level; 1161 var previousHeaderLevel = this.sectionNumbers.length; 1162 if (thisHeaderLevel > previousHeaderLevel) { 1163 // Fill missing section number 1164 var levelDiff = thisHeaderLevel - previousHeaderLevel; 1165 for (var j = 0; j < levelDiff; ++j) { 1166 this.sectionNumbers[thisHeaderLevel - 1 - j] = 0; // Extend 1167 } 1168 } else if (thisHeaderLevel < previousHeaderLevel) { 1169 this.sectionNumbers.length = thisHeaderLevel; // Collapse 1170 } 1171 this.sectionNumbers[thisHeaderLevel - 1]++; 1172 if (this.documentOptions.num) 1173 sectionNumberText = this.sectionNumbers.join("."); 1174 node.sectionNumberText = sectionNumberText; // Can be used in ToC 1175 } 1176 1177 text = this.convertHeader(node, childText, auxData, 1178 taskStatus, sectionNumberText); 1179 1180 if (recordHeader) 1181 this.headers.push(node); 1182 break; 1183 case Node.types.orderedList: 1184 text = this.convertOrderedList(node, childText, auxData); 1185 break; 1186 case Node.types.unorderedList: 1187 text = this.convertUnorderedList(node, childText, auxData); 1188 break; 1189 case Node.types.definitionList: 1190 text = this.convertDefinitionList(node, childText, auxData); 1191 break; 1192 case Node.types.listElement: 1193 if (node.isDefinitionList) { 1194 var termText = this.convertNodesInternal(node.term, recordHeader, insideCodeElement); 1195 text = this.convertDefinitionItem(node, childText, auxData, 1196 termText, childText); 1197 } else { 1198 text = this.convertListItem(node, childText, auxData); 1199 } 1200 break; 1201 case Node.types.paragraph: 1202 text = this.convertParagraph(node, childText, auxData); 1203 break; 1204 case Node.types.preformatted: 1205 text = this.convertPreformatted(node, childText, auxData); 1206 break; 1207 case Node.types.table: 1208 text = this.convertTable(node, childText, auxData); 1209 break; 1210 case Node.types.tableRow: 1211 text = this.convertTableRow(node, childText, auxData); 1212 break; 1213 case Node.types.tableCell: 1214 if (node.isHeader) { 1215 if (childText) 1216 text = this.convertTableHeader(node, childText, auxData); 1217 } else 1218 text = this.convertTableCell(node, childText, auxData); 1219 break; 1220 case Node.types.horizontalRule: 1221 text = this.convertHorizontalRule(node, childText, auxData); 1222 break; 1223 // ============================================================ // 1224 // Inline 1225 // ============================================================ // 1226 case Node.types.inlineContainer: 1227 text = this.convertInlineContainer(node, childText, auxData); 1228 break; 1229 case Node.types.bold: 1230 text = this.convertBold(node, childText, auxData); 1231 break; 1232 case Node.types.italic: 1233 text = this.convertItalic(node, childText, auxData); 1234 break; 1235 case Node.types.underline: 1236 text = this.convertUnderline(node, childText, auxData); 1237 break; 1238 case Node.types.code: 1239 text = this.convertCode(node, childText, auxData); 1240 break; 1241 case Node.types.dashed: 1242 text = this.convertDashed(node, childText, auxData); 1243 break; 1244 case Node.types.link: 1245 text = this.convertLink(node, childText, auxData); 1246 break; 1247 case Node.types.property: 1248 text = this.convertProperty(node, childText, auxData); 1249 break; 1250 case Node.types.directive: 1251 switch (node.directiveName) { 1252 case "quote": 1253 text = this.convertQuote(node, childText, auxData); 1254 break; 1255 case "example": 1256 text = this.convertExample(node, childText, auxData); 1257 break; 1258 case "src": 1259 text = this.convertSrc(node, childText, auxData); 1260 break; 1261 case "html": 1262 case "html:": 1263 text = this.convertHTML(node, childText, auxData); 1264 break; 1265 default: 1266 if (this.exportOptions.customDirectiveHandler && 1267 this.exportOptions.customDirectiveHandler[node.directiveName]) { 1268 text = this.exportOptions.customDirectiveHandler[node.directiveName]( 1269 node, childText, auxData 1270 ); 1271 } else { 1272 text = childText; 1273 } 1274 } 1275 break; 1276 case Node.types.text: 1277 text = this.convertText(node.value, insideCodeElement); 1278 break; 1279 default: 1280 throw Error("Unknown node type: " + node.type); 1281 } 1282 1283 if (typeof this.postProcess === "function") { 1284 text = this.postProcess(node, text, insideCodeElement); 1285 } 1286 1287 return text; 1288 }, 1289 1290 convertText: function (text, insideCodeElement) { 1291 var escapedText = this.escapeSpecialChars(text, insideCodeElement); 1292 1293 if (!this.exportOptions.suppressSubScriptHandling && !insideCodeElement) { 1294 escapedText = this.makeSubscripts(escapedText, insideCodeElement); 1295 } 1296 if (!this.exportOptions.suppressAutoLink) { 1297 escapedText = this.linkURL(escapedText); 1298 } 1299 1300 return escapedText; 1301 }, 1302 1303 // By default, ignore html 1304 convertHTML: function (node, childText, auxData) { 1305 return childText; 1306 }, 1307 1308 convertNodesInternal: function (nodes, recordHeader, insideCodeElement) { 1309 var nodesTexts = []; 1310 for (var i = 0; i < nodes.length; ++i) { 1311 var node = nodes[i]; 1312 var nodeText = this.convertNode(node, recordHeader, insideCodeElement); 1313 nodesTexts.push(nodeText); 1314 } 1315 return this.combineNodesTexts(nodesTexts); 1316 }, 1317 1318 convertHeaderBlock: function (headerBlock, recordHeader) { 1319 throw Error("convertHeaderBlock is not implemented"); 1320 }, 1321 1322 convertHeaderTree: function (headerTree, recordHeader) { 1323 return this.convertHeaderBlock(headerTree, recordHeader); 1324 }, 1325 1326 convertNodesToHeaderTree: function (nodes, nextBlockBegin, blockHeader) { 1327 var childBlocks = []; 1328 var childNodes = []; 1329 1330 if (typeof nextBlockBegin === "undefined") { 1331 nextBlockBegin = 0; 1332 } 1333 if (typeof blockHeader === "undefined") { 1334 blockHeader = null; 1335 } 1336 1337 for (var i = nextBlockBegin; i < nodes.length;) { 1338 var node = nodes[i]; 1339 1340 var isHeader = node.type === Node.types.header; 1341 1342 if (!isHeader) { 1343 childNodes.push(node); 1344 i = i + 1; 1345 continue; 1346 } 1347 1348 // Header 1349 if (blockHeader && node.level <= blockHeader.level) { 1350 // Finish Block 1351 break; 1352 } else { 1353 // blockHeader.level < node.level 1354 // Begin child block 1355 var childBlock = this.convertNodesToHeaderTree(nodes, i + 1, node); 1356 childBlocks.push(childBlock); 1357 i = childBlock.nextIndex; 1358 } 1359 } 1360 1361 // Finish block 1362 return { 1363 header: blockHeader, 1364 childNodes: childNodes, 1365 nextIndex: i, 1366 childBlocks: childBlocks 1367 }; 1368 }, 1369 1370 convertNodes: function (nodes, recordHeader, insideCodeElement) { 1371 return this.convertNodesInternal(nodes, recordHeader, insideCodeElement); 1372 }, 1373 1374 combineNodesTexts: function (nodesTexts) { 1375 return nodesTexts.join(""); 1376 }, 1377 1378 getNodeTextContent: function (node) { 1379 if (node.type === Node.types.text) 1380 return this.escapeSpecialChars(node.value); 1381 else 1382 return node.children ? node.children.map(this.getNodeTextContent, this).join("") : ""; 1383 }, 1384 1385 // @Override 1386 escapeSpecialChars: function (text) { 1387 throw Error("Implement escapeSpecialChars"); 1388 }, 1389 1390 // http://daringfireball.net/2010/07/improved_regex_for_matching_urls 1391 urlPattern: /\b(?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’])/ig, 1392 1393 // @Override 1394 linkURL: function (text) { 1395 var self = this; 1396 return text.replace(this.urlPattern, function (matched) { 1397 if (matched.indexOf("://") < 0) 1398 matched = "http://" + matched; 1399 return self.makeLink(matched); 1400 }); 1401 }, 1402 1403 makeLink: function (url) { 1404 throw Error("Implement makeLink"); 1405 }, 1406 1407 makeSubscripts: function (text) { 1408 if (this.documentOptions["^"] === "{}") 1409 return text.replace(/\b([^_ \t]*)_{([^}]*)}/g, 1410 this.makeSubscript); 1411 else if (this.documentOptions["^"]) 1412 return text.replace(/\b([^_ \t]*)_([^_]*)\b/g, 1413 this.makeSubscript); 1414 else 1415 return text; 1416 }, 1417 1418 makeSubscript: function (match, body, subscript) { 1419 throw Error("Implement makeSubscript"); 1420 }, 1421 1422 stripParametersFromURL: function (url) { 1423 return url.replace(/\?.*$/, ""); 1424 }, 1425 1426 imageExtensionPattern: new RegExp("(" + [ 1427 "bmp", "png", "jpeg", "jpg", "gif", "tiff", 1428 "tif", "xbm", "xpm", "pbm", "pgm", "ppm", "svg" 1429 ].join("|") + ")$", "i") 1430 }; 1431 1432 if (typeof exports !== "undefined") 1433 exports.Converter = Converter; 1434 1435 // var Converter = require("./converter.js").Converter; 1436 // var Node = require("../node.js").Node; 1437 1438 function ConverterHTML(orgDocument, exportOptions) { 1439 this.initialize(orgDocument, exportOptions); 1440 this.result = this.convert(); 1441 } 1442 1443 ConverterHTML.prototype = { 1444 __proto__: Converter.prototype, 1445 1446 convert: function () { 1447 var title = this.orgDocument.title ? this.convertNode(this.orgDocument.title) : this.untitled; 1448 var titleHTML = this.tag("h" + Math.max(Number(this.headerOffset), 1), title); 1449 var contentHTML = this.convertNodes(this.orgDocument.nodes, true /* record headers */); 1450 var toc = ''; 1451 var tocHTML = ''; 1452 if (this.documentOptions.toc) { 1453 toc = this.computeToc(this.documentOptions["toc"]); 1454 tocHTML = this.tocToHTML(toc); 1455 } 1456 return { 1457 title: title, 1458 titleHTML: titleHTML, 1459 contentHTML: contentHTML, 1460 tocHTML: tocHTML, 1461 toc: toc, 1462 toString: function () { 1463 return titleHTML + tocHTML + "\n" + contentHTML; 1464 } 1465 }; 1466 }, 1467 1468 tocToHTML: function (toc) { 1469 function tocToHTMLFunction(tocList) { 1470 var html = ""; 1471 for (var i = 0; i < tocList.length; ++i) { 1472 var tocItem = tocList[i]; 1473 var sectionNumberText = tocItem.headerNode.sectionNumberText; 1474 var sectionNumber = this.documentOptions.num ? 1475 this.inlineTag("span", sectionNumberText, { 1476 "class": "section-number" 1477 }) : ""; 1478 var header = this.getNodeTextContent(tocItem.headerNode); 1479 var headerLink = this.inlineTag("a", sectionNumber + header, { 1480 href: "#header-" + sectionNumberText.replace(/\./g, "-") 1481 }); 1482 var subList = tocItem.childTocs.length ? tocToHTMLFunction.call(this, tocItem.childTocs) : ""; 1483 html += this.tag("li", headerLink + subList); 1484 } 1485 return this.tag("ul", html); 1486 } 1487 1488 return tocToHTMLFunction.call(this, toc); 1489 }, 1490 1491 computeAuxDataForNode: function (node) { 1492 while (node.parent && 1493 node.parent.type === Node.types.inlineContainer) { 1494 node = node.parent; 1495 } 1496 var attributesNode = node.previousSibling; 1497 var attributesText = ""; 1498 while (attributesNode && 1499 attributesNode.type === Node.types.directive && 1500 attributesNode.directiveName === "attr_html:") { 1501 attributesText += attributesNode.directiveRawValue + " "; 1502 attributesNode = attributesNode.previousSibling; 1503 } 1504 return attributesText; 1505 }, 1506 1507 // Method to construct org-js generated class 1508 orgClassName: function (className) { 1509 return this.exportOptions.htmlClassPrefix ? 1510 this.exportOptions.htmlClassPrefix + className 1511 : className; 1512 }, 1513 1514 // Method to construct org-js generated id 1515 orgId: function (id) { 1516 return this.exportOptions.htmlIdPrefix ? 1517 this.exportOptions.htmlIdPrefix + id 1518 : id; 1519 }, 1520 1521 // ---------------------------------------------------- 1522 // Node conversion 1523 // ---------------------------------------------------- 1524 1525 convertHeader: function (node, childText, auxData, 1526 taskStatus, sectionNumberText) { 1527 var headerAttributes = {}; 1528 1529 if (taskStatus) { 1530 childText = this.inlineTag("span", childText.substring(0, 4), { 1531 "class": "task-status " + taskStatus 1532 }) + childText.substring(5); 1533 } 1534 1535 if (sectionNumberText) { 1536 childText = this.inlineTag("span", sectionNumberText, { 1537 "class": "section-number" 1538 }) + childText; 1539 headerAttributes["id"] = "header-" + sectionNumberText.replace(/\./g, "-"); 1540 } 1541 1542 if (taskStatus) 1543 headerAttributes["class"] = "task-status " + taskStatus; 1544 1545 return this.tag("h" + (this.headerOffset + node.level), 1546 childText, headerAttributes, auxData); 1547 }, 1548 1549 convertOrderedList: function (node, childText, auxData) { 1550 return this.tag("ol", childText, null, auxData); 1551 }, 1552 1553 convertUnorderedList: function (node, childText, auxData) { 1554 return this.tag("ul", childText, null, auxData); 1555 }, 1556 1557 convertDefinitionList: function (node, childText, auxData) { 1558 return this.tag("dl", childText, null, auxData); 1559 }, 1560 1561 convertDefinitionItem: function (node, childText, auxData, 1562 term, definition) { 1563 return this.tag("dt", term) + this.tag("dd", definition); 1564 }, 1565 1566 convertListItem: function (node, childText, auxData) { 1567 if (this.exportOptions.suppressCheckboxHandling) { 1568 return this.tag("li", childText, null, auxData); 1569 } else { 1570 var listItemAttributes = {}; 1571 var listItemText = childText; 1572 // Embed checkbox 1573 if (/^\s*\[(X| |-)\]([\s\S]*)/.exec(listItemText)) { 1574 listItemText = RegExp.$2 ; 1575 var checkboxIndicator = RegExp.$1; 1576 1577 var checkboxAttributes = { type: "checkbox" }; 1578 switch (checkboxIndicator) { 1579 case "X": 1580 checkboxAttributes["checked"] = "true"; 1581 listItemAttributes["data-checkbox-status"] = "done"; 1582 break; 1583 case "-": 1584 listItemAttributes["data-checkbox-status"] = "intermediate"; 1585 break; 1586 default: 1587 listItemAttributes["data-checkbox-status"] = "undone"; 1588 break; 1589 } 1590 1591 listItemText = this.inlineTag("input", null, checkboxAttributes) + listItemText; 1592 } 1593 1594 return this.tag("li", listItemText, listItemAttributes, auxData); 1595 } 1596 }, 1597 1598 convertParagraph: function (node, childText, auxData) { 1599 return this.tag("p", childText, null, auxData); 1600 }, 1601 1602 convertPreformatted: function (node, childText, auxData) { 1603 return this.tag("pre", childText, null, auxData); 1604 }, 1605 1606 convertTable: function (node, childText, auxData) { 1607 return this.tag("table", this.tag("tbody", childText), null, auxData); 1608 }, 1609 1610 convertTableRow: function (node, childText, auxData) { 1611 return this.tag("tr", childText); 1612 }, 1613 1614 convertTableHeader: function (node, childText, auxData) { 1615 return this.tag("th", childText); 1616 }, 1617 1618 convertTableCell: function (node, childText, auxData) { 1619 return this.tag("td", childText); 1620 }, 1621 1622 convertHorizontalRule: function (node, childText, auxData) { 1623 return this.tag("hr", null, null, auxData); 1624 }, 1625 1626 convertInlineContainer: function (node, childText, auxData) { 1627 return childText; 1628 }, 1629 1630 convertBold: function (node, childText, auxData) { 1631 return this.inlineTag("b", childText); 1632 }, 1633 1634 convertItalic: function (node, childText, auxData) { 1635 return this.inlineTag("i", childText); 1636 }, 1637 1638 convertUnderline: function (node, childText, auxData) { 1639 return this.inlineTag("span", childText, { 1640 style: "text-decoration:underline;" 1641 }); 1642 }, 1643 1644 convertCode: function (node, childText, auxData) { 1645 return this.inlineTag("code", childText); 1646 }, 1647 1648 convertDashed: function (node, childText, auxData) { 1649 return this.inlineTag("del", childText); 1650 }, 1651 1652 convertLink: function (node, childText, auxData) { 1653 var srcParameterStripped = this.stripParametersFromURL(node.src); 1654 if (this.imageExtensionPattern.exec(srcParameterStripped)) { 1655 var imgText = this.getNodeTextContent(node); 1656 return this.inlineTag("img", null, { 1657 src: node.src, 1658 alt: imgText, 1659 title: imgText 1660 }, auxData); 1661 } else { 1662 return this.inlineTag("a", childText, { href: node.src }); 1663 } 1664 }, 1665 1666 convertQuote: function (node, childText, auxData) { 1667 return this.tag("blockquote", childText, null, auxData); 1668 }, 1669 1670 convertExample: function (node, childText, auxData) { 1671 return this.tag("pre", childText, null, auxData); 1672 }, 1673 1674 convertSrc: function (node, childText, auxData) { 1675 var codeLanguage = node.directiveArguments.length 1676 ? node.directiveArguments[0] 1677 : "unknown"; 1678 childText = this.tag("code", childText, { 1679 "class": "language-" + codeLanguage 1680 }, auxData); 1681 return this.tag("pre", childText, { 1682 "class": "prettyprint" 1683 }); 1684 }, 1685 1686 // @override 1687 convertHTML: function (node, childText, auxData) { 1688 if (node.directiveName === "html:") { 1689 return node.directiveRawValue; 1690 } else if (node.directiveName === "html") { 1691 return node.children.map(function (textNode) { 1692 return textNode.value; 1693 }).join("\n"); 1694 } else { 1695 return childText; 1696 } 1697 }, 1698 1699 // @implement 1700 convertHeaderBlock: function (headerBlock, level, index) { 1701 level = level || 0; 1702 index = index || 0; 1703 1704 var contents = []; 1705 1706 var headerNode = headerBlock.header; 1707 if (headerNode) { 1708 contents.push(this.convertNode(headerNode)); 1709 } 1710 1711 var blockContent = this.convertNodes(headerBlock.childNodes); 1712 contents.push(blockContent); 1713 1714 var childBlockContent = headerBlock.childBlocks 1715 .map(function (block, idx) { 1716 return this.convertHeaderBlock(block, level + 1, idx); 1717 }, this) 1718 .join("\n"); 1719 contents.push(childBlockContent); 1720 1721 var contentsText = contents.join("\n"); 1722 1723 if (headerNode) { 1724 return this.tag("section", "\n" + contents.join("\n"), { 1725 "class": "block block-level-" + level 1726 }); 1727 } else { 1728 return contentsText; 1729 } 1730 }, 1731 1732 // ---------------------------------------------------- 1733 // Supplemental methods 1734 // ---------------------------------------------------- 1735 1736 replaceMap: { 1737 // [replacing pattern, predicate] 1738 "&": ["&", null], 1739 "<": ["<", null], 1740 ">": [">", null], 1741 '"': [""", null], 1742 "'": ["'", null], 1743 "->": ["➔", function (text, insideCodeElement) { 1744 return this.exportOptions.translateSymbolArrow && !insideCodeElement; 1745 }] 1746 }, 1747 1748 replaceRegexp: null, 1749 1750 // @implement @override 1751 escapeSpecialChars: function (text, insideCodeElement) { 1752 if (!this.replaceRegexp) { 1753 this.replaceRegexp = new RegExp(Object.keys(this.replaceMap).join("|"), "g"); 1754 } 1755 1756 var replaceMap = this.replaceMap; 1757 var self = this; 1758 return text.replace(this.replaceRegexp, function (matched) { 1759 if (!replaceMap[matched]) { 1760 throw Error("escapeSpecialChars: Invalid match"); 1761 } 1762 1763 var predicate = replaceMap[matched][1]; 1764 if (typeof predicate === "function" && 1765 !predicate.call(self, text, insideCodeElement)) { 1766 // Not fullfill the predicate 1767 return matched; 1768 } 1769 1770 return replaceMap[matched][0]; 1771 }); 1772 }, 1773 1774 // @implement 1775 postProcess: function (node, currentText, insideCodeElement) { 1776 if (this.exportOptions.exportFromLineNumber && 1777 typeof node.fromLineNumber === "number") { 1778 // Wrap with line number information 1779 currentText = this.inlineTag("div", currentText, { 1780 "data-line-number": node.fromLineNumber 1781 }); 1782 } 1783 return currentText; 1784 }, 1785 1786 // @implement 1787 makeLink: function (url) { 1788 return "<a href=\"" + url + "\">" + decodeURIComponent(url) + "</a>"; 1789 }, 1790 1791 // @implement 1792 makeSubscript: function (match, body, subscript) { 1793 return "<span class=\"org-subscript-parent\">" + 1794 body + 1795 "</span><span class=\"org-subscript-child\">" + 1796 subscript + 1797 "</span>"; 1798 }, 1799 1800 // ---------------------------------------------------- 1801 // Specific methods 1802 // ---------------------------------------------------- 1803 1804 attributesObjectToString: function (attributesObject) { 1805 var attributesString = ""; 1806 for (var attributeName in attributesObject) { 1807 if (attributesObject.hasOwnProperty(attributeName)) { 1808 var attributeValue = attributesObject[attributeName]; 1809 // To avoid id/class name conflicts with other frameworks, 1810 // users can add arbitrary prefix to org-js generated 1811 // ids/classes via exportOptions. 1812 if (attributeName === "class") { 1813 attributeValue = this.orgClassName(attributeValue); 1814 } else if (attributeName === "id") { 1815 attributeValue = this.orgId(attributeValue); 1816 } 1817 attributesString += " " + attributeName + "=\"" + attributeValue + "\""; 1818 } 1819 } 1820 return attributesString; 1821 }, 1822 1823 inlineTag: function (name, innerText, attributesObject, auxAttributesText) { 1824 attributesObject = attributesObject || {}; 1825 1826 var htmlString = "<" + name; 1827 // TODO: check duplicated attributes 1828 if (auxAttributesText) 1829 htmlString += " " + auxAttributesText; 1830 htmlString += this.attributesObjectToString(attributesObject); 1831 1832 if (innerText === null) 1833 return htmlString + "/>"; 1834 1835 htmlString += ">" + innerText + "</" + name + ">"; 1836 1837 return htmlString; 1838 }, 1839 1840 tag: function (name, innerText, attributesObject, auxAttributesText) { 1841 return this.inlineTag(name, innerText, attributesObject, auxAttributesText) + "\n"; 1842 } 1843 }; 1844 1845 if (typeof exports !== "undefined") 1846 exports.ConverterHTML = ConverterHTML; 1847 1848 return exports; 1849 })(); 1850 1851 Object.assign(exports, Org)