org.js 52 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650
  1. // Generated by export.rb at Wed Oct 8 13:35:23 UTC 2014
  2. /*
  3. Copyright (c) 2014 Masafumi Oyamada
  4. Permission is hereby granted, free of charge, to any person obtaining a copy
  5. of this software and associated documentation files (the "Software"), to deal
  6. in the Software without restriction, including without limitation the rights
  7. to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. copies of the Software, and to permit persons to whom the Software is
  9. furnished to do so, subject to the following conditions:
  10. The above copyright notice and this permission notice shall be included in
  11. all copies or substantial portions of the Software.
  12. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  13. IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  14. FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  15. AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  16. LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  17. OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
  18. THE SOFTWARE.
  19. */
  20. var Org = (function () {
  21. var exports = {};
  22. // ------------------------------------------------------------
  23. // Syntax
  24. // ------------------------------------------------------------
  25. var Syntax = {
  26. rules: {},
  27. define: function (name, syntax) {
  28. this.rules[name] = syntax;
  29. var methodName = "is" + name.substring(0, 1).toUpperCase() + name.substring(1);
  30. this[methodName] = function (line) {
  31. return this.rules[name].exec(line);
  32. };
  33. }
  34. };
  35. Syntax.define("header", /^(\*+)\s+(.*)$/); // m[1] => level, m[2] => content
  36. Syntax.define("preformatted", /^(\s*):(?: (.*)$|$)/); // m[1] => indentation, m[2] => content
  37. Syntax.define("unorderedListElement", /^(\s*)(?:-|\+|\s+\*)\s+(.*)$/); // m[1] => indentation, m[2] => content
  38. Syntax.define("orderedListElement", /^(\s*)(\d+)(?:\.|\))\s+(.*)$/); // m[1] => indentation, m[2] => number, m[3] => content
  39. Syntax.define("tableSeparator", /^(\s*)\|((?:\+|-)*?)\|?$/); // m[1] => indentation, m[2] => content
  40. Syntax.define("tableRow", /^(\s*)\|(.*?)\|?$/); // m[1] => indentation, m[2] => content
  41. Syntax.define("blank", /^$/);
  42. Syntax.define("horizontalRule", /^(\s*)-{5,}$/); //
  43. Syntax.define("directive", /^(\s*)#\+(?:(begin|end)_)?(.*)$/i); // m[1] => indentation, m[2] => type, m[3] => content
  44. Syntax.define("comment", /^(\s*)#(.*)$/);
  45. Syntax.define("line", /^(\s*)(.*)$/);
  46. // ------------------------------------------------------------
  47. // Token
  48. // ------------------------------------------------------------
  49. function Token() {
  50. }
  51. Token.prototype = {
  52. isListElement: function () {
  53. return this.type === Lexer.tokens.orderedListElement ||
  54. this.type === Lexer.tokens.unorderedListElement;
  55. },
  56. isTableElement: function () {
  57. return this.type === Lexer.tokens.tableSeparator ||
  58. this.type === Lexer.tokens.tableRow;
  59. }
  60. };
  61. // ------------------------------------------------------------
  62. // Lexer
  63. // ------------------------------------------------------------
  64. function Lexer(stream) {
  65. this.stream = stream;
  66. this.tokenStack = [];
  67. }
  68. Lexer.prototype = {
  69. tokenize: function (line) {
  70. var token = new Token();
  71. token.fromLineNumber = this.stream.lineNumber;
  72. if (Syntax.isHeader(line)) {
  73. token.type = Lexer.tokens.header;
  74. token.indentation = 0;
  75. token.content = RegExp.$2;
  76. // specific
  77. token.level = RegExp.$1.length;
  78. } else if (Syntax.isPreformatted(line)) {
  79. token.type = Lexer.tokens.preformatted;
  80. token.indentation = RegExp.$1.length;
  81. token.content = RegExp.$2;
  82. } else if (Syntax.isUnorderedListElement(line)) {
  83. token.type = Lexer.tokens.unorderedListElement;
  84. token.indentation = RegExp.$1.length;
  85. token.content = RegExp.$2;
  86. } else if (Syntax.isOrderedListElement(line)) {
  87. token.type = Lexer.tokens.orderedListElement;
  88. token.indentation = RegExp.$1.length;
  89. token.content = RegExp.$3;
  90. // specific
  91. token.number = RegExp.$2;
  92. } else if (Syntax.isTableSeparator(line)) {
  93. token.type = Lexer.tokens.tableSeparator;
  94. token.indentation = RegExp.$1.length;
  95. token.content = RegExp.$2;
  96. } else if (Syntax.isTableRow(line)) {
  97. token.type = Lexer.tokens.tableRow;
  98. token.indentation = RegExp.$1.length;
  99. token.content = RegExp.$2;
  100. } else if (Syntax.isBlank(line)) {
  101. token.type = Lexer.tokens.blank;
  102. token.indentation = 0;
  103. token.content = null;
  104. } else if (Syntax.isHorizontalRule(line)) {
  105. token.type = Lexer.tokens.horizontalRule;
  106. token.indentation = RegExp.$1.length;
  107. token.content = null;
  108. } else if (Syntax.isDirective(line)) {
  109. token.type = Lexer.tokens.directive;
  110. token.indentation = RegExp.$1.length;
  111. token.content = RegExp.$3;
  112. // decide directive type (begin, end or oneshot)
  113. var directiveTypeString = RegExp.$2;
  114. if (/^begin/i.test(directiveTypeString))
  115. token.beginDirective = true;
  116. else if (/^end/i.test(directiveTypeString))
  117. token.endDirective = true;
  118. else
  119. token.oneshotDirective = true;
  120. } else if (Syntax.isComment(line)) {
  121. token.type = Lexer.tokens.comment;
  122. token.indentation = RegExp.$1.length;
  123. token.content = RegExp.$2;
  124. } else if (Syntax.isLine(line)) {
  125. token.type = Lexer.tokens.line;
  126. token.indentation = RegExp.$1.length;
  127. token.content = RegExp.$2;
  128. } else {
  129. throw new Error("SyntaxError: Unknown line: " + line);
  130. }
  131. return token;
  132. },
  133. pushToken: function (token) {
  134. this.tokenStack.push(token);
  135. },
  136. pushDummyTokenByType: function (type) {
  137. var token = new Token();
  138. token.type = type;
  139. this.tokenStack.push(token);
  140. },
  141. peekStackedToken: function () {
  142. return this.tokenStack.length > 0 ?
  143. this.tokenStack[this.tokenStack.length - 1] : null;
  144. },
  145. getStackedToken: function () {
  146. return this.tokenStack.length > 0 ?
  147. this.tokenStack.pop() : null;
  148. },
  149. peekNextToken: function () {
  150. return this.peekStackedToken() ||
  151. this.tokenize(this.stream.peekNextLine());
  152. },
  153. getNextToken: function () {
  154. return this.getStackedToken() ||
  155. this.tokenize(this.stream.getNextLine());
  156. },
  157. hasNext: function () {
  158. return this.stream.hasNext();
  159. },
  160. getLineNumber: function () {
  161. return this.stream.lineNumber;
  162. }
  163. };
  164. Lexer.tokens = {};
  165. [
  166. "header",
  167. "orderedListElement",
  168. "unorderedListElement",
  169. "tableRow",
  170. "tableSeparator",
  171. "preformatted",
  172. "line",
  173. "horizontalRule",
  174. "blank",
  175. "directive",
  176. "comment"
  177. ].forEach(function (tokenName, i) {
  178. Lexer.tokens[tokenName] = i;
  179. });
  180. // ------------------------------------------------------------
  181. // Exports
  182. // ------------------------------------------------------------
  183. if (typeof exports !== "undefined")
  184. exports.Lexer = Lexer;
  185. function PrototypeNode(type, children) {
  186. this.type = type;
  187. this.children = [];
  188. if (children) {
  189. for (var i = 0, len = children.length; i < len; ++i) {
  190. this.appendChild(children[i]);
  191. }
  192. }
  193. }
  194. PrototypeNode.prototype = {
  195. previousSibling: null,
  196. parent: null,
  197. get firstChild() {
  198. return this.children.length < 1 ?
  199. null : this.children[0];
  200. },
  201. get lastChild() {
  202. return this.children.length < 1 ?
  203. null : this.children[this.children.length - 1];
  204. },
  205. appendChild: function (newChild) {
  206. var previousSibling = this.children.length < 1 ?
  207. null : this.lastChild;
  208. this.children.push(newChild);
  209. newChild.previousSibling = previousSibling;
  210. newChild.parent = this;
  211. },
  212. toString: function () {
  213. var string = "<" + this.type + ">";
  214. if (typeof this.value !== "undefined") {
  215. string += " " + this.value;
  216. } else if (this.children) {
  217. string += "\n" + this.children.map(function (child, idx) {
  218. return "#" + idx + " " + child.toString();
  219. }).join("\n").split("\n").map(function (line) {
  220. return " " + line;
  221. }).join("\n");
  222. }
  223. return string;
  224. }
  225. };
  226. var Node = {
  227. types: {},
  228. define: function (name, postProcess) {
  229. this.types[name] = name;
  230. var methodName = "create" + name.substring(0, 1).toUpperCase() + name.substring(1);
  231. var postProcessGiven = typeof postProcess === "function";
  232. this[methodName] = function (children, options) {
  233. var node = new PrototypeNode(name, children);
  234. if (postProcessGiven)
  235. postProcess(node, options || {});
  236. return node;
  237. };
  238. }
  239. };
  240. Node.define("text", function (node, options) {
  241. node.value = options.value;
  242. });
  243. Node.define("header", function (node, options) {
  244. node.level = options.level;
  245. });
  246. Node.define("orderedList");
  247. Node.define("unorderedList");
  248. Node.define("definitionList");
  249. Node.define("listElement");
  250. Node.define("paragraph");
  251. Node.define("preformatted");
  252. Node.define("table");
  253. Node.define("tableRow");
  254. Node.define("tableCell");
  255. Node.define("horizontalRule");
  256. Node.define("directive");
  257. // Inline
  258. Node.define("inlineContainer");
  259. Node.define("bold");
  260. Node.define("italic");
  261. Node.define("underline");
  262. Node.define("code");
  263. Node.define("verbatim");
  264. Node.define("dashed");
  265. Node.define("link", function (node, options) {
  266. node.src = options.src;
  267. });
  268. if (typeof exports !== "undefined")
  269. exports.Node = Node;
  270. function Stream(sequence) {
  271. this.sequences = sequence.split(/\r?\n/);
  272. this.totalLines = this.sequences.length;
  273. this.lineNumber = 0;
  274. }
  275. Stream.prototype.peekNextLine = function () {
  276. return this.hasNext() ? this.sequences[this.lineNumber] : null;
  277. };
  278. Stream.prototype.getNextLine = function () {
  279. return this.hasNext() ? this.sequences[this.lineNumber++] : null;
  280. };
  281. Stream.prototype.hasNext = function () {
  282. return this.lineNumber < this.totalLines;
  283. };
  284. if (typeof exports !== "undefined") {
  285. exports.Stream = Stream;
  286. }
  287. // var Stream = require("./stream.js").Stream;
  288. // var Lexer = require("./lexer.js").Lexer;
  289. // var Node = require("./node.js").Node;
  290. function Parser() {
  291. this.inlineParser = new InlineParser();
  292. }
  293. Parser.parseStream = function (stream, options) {
  294. var parser = new Parser();
  295. parser.initStatus(stream, options);
  296. parser.parseNodes();
  297. return parser.nodes;
  298. };
  299. Parser.prototype = {
  300. initStatus: function (stream, options) {
  301. if (typeof stream === "string")
  302. stream = new Stream(stream);
  303. this.lexer = new Lexer(stream);
  304. this.nodes = [];
  305. this.options = {
  306. toc: true,
  307. num: true,
  308. "^": "{}",
  309. multilineCell: false
  310. };
  311. // Override option values
  312. if (options && typeof options === "object") {
  313. for (var key in options) {
  314. this.options[key] = options[key];
  315. }
  316. }
  317. this.document = {
  318. options: this.options,
  319. convert: function (ConverterClass, exportOptions) {
  320. var converter = new ConverterClass(this, exportOptions);
  321. return converter.result;
  322. }
  323. };
  324. },
  325. parse: function (stream, options) {
  326. this.initStatus(stream, options);
  327. this.parseDocument();
  328. this.document.nodes = this.nodes;
  329. return this.document;
  330. },
  331. createErrorReport: function (message) {
  332. return new Error(message + " at line " + this.lexer.getLineNumber());
  333. },
  334. skipBlank: function () {
  335. var blankToken = null;
  336. while (this.lexer.peekNextToken().type === Lexer.tokens.blank)
  337. blankToken = this.lexer.getNextToken();
  338. return blankToken;
  339. },
  340. setNodeOriginFromToken: function (node, token) {
  341. node.fromLineNumber = token.fromLineNumber;
  342. return node;
  343. },
  344. appendNode: function (newNode) {
  345. var previousSibling = this.nodes.length > 0 ? this.nodes[this.nodes.length - 1] : null;
  346. this.nodes.push(newNode);
  347. newNode.previousSibling = previousSibling;
  348. },
  349. // ------------------------------------------------------------
  350. // <Document> ::= <Element>*
  351. // ------------------------------------------------------------
  352. parseDocument: function () {
  353. this.parseTitle();
  354. this.parseNodes();
  355. },
  356. parseNodes: function () {
  357. while (this.lexer.hasNext()) {
  358. var element = this.parseElement();
  359. if (element) this.appendNode(element);
  360. }
  361. },
  362. parseTitle: function () {
  363. this.skipBlank();
  364. if (this.lexer.hasNext() &&
  365. this.lexer.peekNextToken().type === Lexer.tokens.line)
  366. this.document.title = this.createTextNode(this.lexer.getNextToken().content);
  367. else
  368. this.document.title = null;
  369. this.lexer.pushDummyTokenByType(Lexer.tokens.blank);
  370. },
  371. // ------------------------------------------------------------
  372. // <Element> ::= (<Header> | <List>
  373. // | <Preformatted> | <Paragraph>
  374. // | <Table>)*
  375. // ------------------------------------------------------------
  376. parseElement: function () {
  377. var element = null;
  378. switch (this.lexer.peekNextToken().type) {
  379. case Lexer.tokens.header:
  380. element = this.parseHeader();
  381. break;
  382. case Lexer.tokens.preformatted:
  383. element = this.parsePreformatted();
  384. break;
  385. case Lexer.tokens.orderedListElement:
  386. case Lexer.tokens.unorderedListElement:
  387. element = this.parseList();
  388. break;
  389. case Lexer.tokens.line:
  390. element = this.parseText();
  391. break;
  392. case Lexer.tokens.tableRow:
  393. case Lexer.tokens.tableSeparator:
  394. element = this.parseTable();
  395. break;
  396. case Lexer.tokens.blank:
  397. this.skipBlank();
  398. if (this.lexer.hasNext()) {
  399. if (this.lexer.peekNextToken().type === Lexer.tokens.line)
  400. element = this.parseParagraph();
  401. else
  402. element = this.parseElement();
  403. }
  404. break;
  405. case Lexer.tokens.horizontalRule:
  406. this.lexer.getNextToken();
  407. element = Node.createHorizontalRule();
  408. break;
  409. case Lexer.tokens.directive:
  410. element = this.parseDirective();
  411. break;
  412. case Lexer.tokens.comment:
  413. // Skip
  414. this.lexer.getNextToken();
  415. break;
  416. default:
  417. throw this.createErrorReport("Unhandled token: " + this.lexer.peekNextToken().type);
  418. }
  419. return element;
  420. },
  421. parseElementBesidesDirectiveEnd: function () {
  422. try {
  423. // Temporary, override the definition of `parseElement`
  424. this.parseElement = this.parseElementBesidesDirectiveEndBody;
  425. return this.parseElement();
  426. } finally {
  427. this.parseElement = this.originalParseElement;
  428. }
  429. },
  430. parseElementBesidesDirectiveEndBody: function () {
  431. if (this.lexer.peekNextToken().type === Lexer.tokens.directive &&
  432. this.lexer.peekNextToken().endDirective) {
  433. return null;
  434. }
  435. return this.originalParseElement();
  436. },
  437. // ------------------------------------------------------------
  438. // <Header>
  439. //
  440. // : preformatted
  441. // : block
  442. // ------------------------------------------------------------
  443. parseHeader: function () {
  444. var headerToken = this.lexer.getNextToken();
  445. var header = Node.createHeader([
  446. this.createTextNode(headerToken.content) // TODO: Parse inline markups
  447. ], { level: headerToken.level });
  448. this.setNodeOriginFromToken(header, headerToken);
  449. return header;
  450. },
  451. // ------------------------------------------------------------
  452. // <Preformatted>
  453. //
  454. // : preformatted
  455. // : block
  456. // ------------------------------------------------------------
  457. parsePreformatted: function () {
  458. var preformattedFirstToken = this.lexer.peekNextToken();
  459. var preformatted = Node.createPreformatted([]);
  460. this.setNodeOriginFromToken(preformatted, preformattedFirstToken);
  461. var textContents = [];
  462. while (this.lexer.hasNext()) {
  463. var token = this.lexer.peekNextToken();
  464. if (token.type !== Lexer.tokens.preformatted ||
  465. token.indentation < preformattedFirstToken.indentation)
  466. break;
  467. this.lexer.getNextToken();
  468. textContents.push(token.content);
  469. }
  470. preformatted.appendChild(this.createTextNode(textContents.join("\n"), true /* no emphasis */));
  471. return preformatted;
  472. },
  473. // ------------------------------------------------------------
  474. // <List>
  475. //
  476. // - foo
  477. // 1. bar
  478. // 2. baz
  479. // ------------------------------------------------------------
  480. // XXX: not consider codes (e.g., =Foo::Bar=)
  481. definitionPattern: /^(.*?) :: *(.*)$/,
  482. parseList: function () {
  483. var rootToken = this.lexer.peekNextToken();
  484. var list;
  485. var isDefinitionList = false;
  486. if (this.definitionPattern.test(rootToken.content)) {
  487. list = Node.createDefinitionList([]);
  488. isDefinitionList = true;
  489. } else {
  490. list = rootToken.type === Lexer.tokens.unorderedListElement ?
  491. Node.createUnorderedList([]) : Node.createOrderedList([]);
  492. }
  493. this.setNodeOriginFromToken(list, rootToken);
  494. while (this.lexer.hasNext()) {
  495. var nextToken = this.lexer.peekNextToken();
  496. if (!nextToken.isListElement() || nextToken.indentation !== rootToken.indentation)
  497. break;
  498. list.appendChild(this.parseListElement(rootToken.indentation, isDefinitionList));
  499. }
  500. return list;
  501. },
  502. unknownDefinitionTerm: "???",
  503. parseListElement: function (rootIndentation, isDefinitionList) {
  504. var listElementToken = this.lexer.getNextToken();
  505. var listElement = Node.createListElement([]);
  506. this.setNodeOriginFromToken(listElement, listElementToken);
  507. listElement.isDefinitionList = isDefinitionList;
  508. if (isDefinitionList) {
  509. var match = this.definitionPattern.exec(listElementToken.content);
  510. listElement.term = [
  511. this.createTextNode(match && match[1] ? match[1] : this.unknownDefinitionTerm)
  512. ];
  513. listElement.appendChild(this.createTextNode(match ? match[2] : listElementToken.content));
  514. } else {
  515. listElement.appendChild(this.createTextNode(listElementToken.content));
  516. }
  517. while (this.lexer.hasNext()) {
  518. var blankToken = this.skipBlank();
  519. if (!this.lexer.hasNext())
  520. break;
  521. var notBlankNextToken = this.lexer.peekNextToken();
  522. if (blankToken && !notBlankNextToken.isListElement())
  523. this.lexer.pushToken(blankToken); // Recover blank token only when next line is not listElement.
  524. if (notBlankNextToken.indentation <= rootIndentation)
  525. break; // end of the list
  526. var element = this.parseElement(); // recursive
  527. if (element)
  528. listElement.appendChild(element);
  529. }
  530. return listElement;
  531. },
  532. // ------------------------------------------------------------
  533. // <Table> ::= <TableRow>+
  534. // ------------------------------------------------------------
  535. parseTable: function () {
  536. var nextToken = this.lexer.peekNextToken();
  537. var table = Node.createTable([]);
  538. this.setNodeOriginFromToken(table, nextToken);
  539. var sawSeparator = false;
  540. var allowMultilineCell = nextToken.type === Lexer.tokens.tableSeparator && this.options.multilineCell;
  541. while (this.lexer.hasNext() &&
  542. (nextToken = this.lexer.peekNextToken()).isTableElement()) {
  543. if (nextToken.type === Lexer.tokens.tableRow) {
  544. var tableRow = this.parseTableRow(allowMultilineCell);
  545. table.appendChild(tableRow);
  546. } else {
  547. // Lexer.tokens.tableSeparator
  548. sawSeparator = true;
  549. this.lexer.getNextToken();
  550. }
  551. }
  552. if (sawSeparator && table.children.length) {
  553. table.children[0].children.forEach(function (cell) {
  554. cell.isHeader = true;
  555. });
  556. }
  557. return table;
  558. },
  559. // ------------------------------------------------------------
  560. // <TableRow> ::= <TableCell>+
  561. // ------------------------------------------------------------
  562. parseTableRow: function (allowMultilineCell) {
  563. var tableRowTokens = [];
  564. while (this.lexer.peekNextToken().type === Lexer.tokens.tableRow) {
  565. tableRowTokens.push(this.lexer.getNextToken());
  566. if (!allowMultilineCell) {
  567. break;
  568. }
  569. }
  570. if (!tableRowTokens.length) {
  571. throw this.createErrorReport("Expected table row");
  572. }
  573. var firstTableRowToken = tableRowTokens.shift();
  574. var tableCellTexts = firstTableRowToken.content.split("|");
  575. tableRowTokens.forEach(function (rowToken) {
  576. rowToken.content.split("|").forEach(function (cellText, cellIdx) {
  577. tableCellTexts[cellIdx] = (tableCellTexts[cellIdx] || "") + "\n" + cellText;
  578. });
  579. });
  580. // TODO: Prepare two pathes: (1)
  581. var tableCells = tableCellTexts.map(
  582. // TODO: consider '|' escape?
  583. function (text) {
  584. return Node.createTableCell(Parser.parseStream(text));
  585. }, this);
  586. return this.setNodeOriginFromToken(Node.createTableRow(tableCells), firstTableRowToken);
  587. },
  588. // ------------------------------------------------------------
  589. // <Directive> ::= "#+.*"
  590. // ------------------------------------------------------------
  591. parseDirective: function () {
  592. var directiveToken = this.lexer.getNextToken();
  593. var directiveNode = this.createDirectiveNodeFromToken(directiveToken);
  594. if (directiveToken.endDirective)
  595. throw this.createErrorReport("Unmatched 'end' directive for " + directiveNode.directiveName);
  596. if (directiveToken.oneshotDirective) {
  597. this.interpretDirective(directiveNode);
  598. return directiveNode;
  599. }
  600. if (!directiveToken.beginDirective)
  601. throw this.createErrorReport("Invalid directive " + directiveNode.directiveName);
  602. // Parse begin ~ end
  603. directiveNode.children = [];
  604. if (this.isVerbatimDirective(directiveNode))
  605. return this.parseDirectiveBlockVerbatim(directiveNode);
  606. else
  607. return this.parseDirectiveBlock(directiveNode);
  608. },
  609. createDirectiveNodeFromToken: function (directiveToken) {
  610. var matched = /^[ ]*([^ ]*)[ ]*(.*)[ ]*$/.exec(directiveToken.content);
  611. var directiveNode = Node.createDirective(null);
  612. this.setNodeOriginFromToken(directiveNode, directiveToken);
  613. directiveNode.directiveName = matched[1].toLowerCase();
  614. directiveNode.directiveArguments = this.parseDirectiveArguments(matched[2]);
  615. directiveNode.directiveOptions = this.parseDirectiveOptions(matched[2]);
  616. directiveNode.directiveRawValue = matched[2];
  617. return directiveNode;
  618. },
  619. isVerbatimDirective: function (directiveNode) {
  620. var directiveName = directiveNode.directiveName;
  621. return directiveName === "src" || directiveName === "example";
  622. },
  623. parseDirectiveBlock: function (directiveNode, verbatim) {
  624. this.lexer.pushDummyTokenByType(Lexer.tokens.blank);
  625. while (this.lexer.hasNext()) {
  626. var nextToken = this.lexer.peekNextToken();
  627. if (nextToken.type === Lexer.tokens.directive &&
  628. nextToken.endDirective &&
  629. this.createDirectiveNodeFromToken(nextToken).directiveName === directiveNode.directiveName) {
  630. // Close directive
  631. this.lexer.getNextToken();
  632. return directiveNode;
  633. }
  634. var element = this.parseElementBesidesDirectiveEnd();
  635. if (element)
  636. directiveNode.appendChild(element);
  637. }
  638. throw this.createErrorReport("Unclosed directive " + directiveNode.directiveName);
  639. },
  640. parseDirectiveBlockVerbatim: function (directiveNode) {
  641. var textContent = [];
  642. while (this.lexer.hasNext()) {
  643. var nextToken = this.lexer.peekNextToken();
  644. if (nextToken.type === Lexer.tokens.directive &&
  645. nextToken.endDirective &&
  646. this.createDirectiveNodeFromToken(nextToken).directiveName === directiveNode.directiveName) {
  647. this.lexer.getNextToken();
  648. directiveNode.appendChild(this.createTextNode(textContent.join("\n"), true));
  649. return directiveNode;
  650. }
  651. textContent.push(this.lexer.stream.getNextLine());
  652. }
  653. throw this.createErrorReport("Unclosed directive " + directiveNode.directiveName);
  654. },
  655. parseDirectiveArguments: function (parameters) {
  656. return parameters.split(/[ ]+/).filter(function (param) {
  657. return param.length && param[0] !== "-";
  658. });
  659. },
  660. parseDirectiveOptions: function (parameters) {
  661. return parameters.split(/[ ]+/).filter(function (param) {
  662. return param.length && param[0] === "-";
  663. });
  664. },
  665. interpretDirective: function (directiveNode) {
  666. // http://orgmode.org/manual/Export-options.html
  667. switch (directiveNode.directiveName) {
  668. case "options:":
  669. this.interpretOptionDirective(directiveNode);
  670. break;
  671. case "title:":
  672. this.document.title = directiveNode.directiveRawValue;
  673. break;
  674. case "author:":
  675. this.document.author = directiveNode.directiveRawValue;
  676. break;
  677. case "email:":
  678. this.document.email = directiveNode.directiveRawValue;
  679. break;
  680. }
  681. },
  682. interpretOptionDirective: function (optionDirectiveNode) {
  683. optionDirectiveNode.directiveArguments.forEach(function (pairString) {
  684. var pair = pairString.split(":");
  685. this.options[pair[0]] = this.convertLispyValue(pair[1]);
  686. }, this);
  687. },
  688. convertLispyValue: function (lispyValue) {
  689. switch (lispyValue) {
  690. case "t":
  691. return true;
  692. case "nil":
  693. return false;
  694. default:
  695. if (/^[0-9]+$/.test(lispyValue))
  696. return parseInt(lispyValue);
  697. return lispyValue;
  698. }
  699. },
  700. // ------------------------------------------------------------
  701. // <Paragraph> ::= <Blank> <Line>*
  702. // ------------------------------------------------------------
  703. parseParagraph: function () {
  704. var paragraphFisrtToken = this.lexer.peekNextToken();
  705. var paragraph = Node.createParagraph([]);
  706. this.setNodeOriginFromToken(paragraph, paragraphFisrtToken);
  707. var textContents = [];
  708. while (this.lexer.hasNext()) {
  709. var nextToken = this.lexer.peekNextToken();
  710. if (nextToken.type !== Lexer.tokens.line
  711. || nextToken.indentation < paragraphFisrtToken.indentation)
  712. break;
  713. this.lexer.getNextToken();
  714. textContents.push(nextToken.content);
  715. }
  716. paragraph.appendChild(this.createTextNode(textContents.join("\n")));
  717. return paragraph;
  718. },
  719. parseText: function (noEmphasis) {
  720. var lineToken = this.lexer.getNextToken();
  721. return this.createTextNode(lineToken.content, noEmphasis);
  722. },
  723. // ------------------------------------------------------------
  724. // <Text> (DOM Like)
  725. // ------------------------------------------------------------
  726. createTextNode: function (text, noEmphasis) {
  727. return noEmphasis ? Node.createText(null, { value: text })
  728. : this.inlineParser.parseEmphasis(text);
  729. }
  730. };
  731. Parser.prototype.originalParseElement = Parser.prototype.parseElement;
  732. // ------------------------------------------------------------
  733. // Parser for Inline Elements
  734. //
  735. // @refs org-emphasis-regexp-components
  736. // ------------------------------------------------------------
  737. function InlineParser() {
  738. this.preEmphasis = " \t\\('\"";
  739. this.postEmphasis = "- \t.,:!?;'\"\\)";
  740. this.borderForbidden = " \t\r\n,\"'";
  741. this.bodyRegexp = "[\\s\\S]*?";
  742. this.markers = "*/_=~+";
  743. this.emphasisPattern = this.buildEmphasisPattern();
  744. this.linkPattern = /\[\[([^\]]*)\](?:\[([^\]]*)\])?\]/g; // \1 => link, \2 => text
  745. }
  746. InlineParser.prototype = {
  747. parseEmphasis: function (text) {
  748. var emphasisPattern = this.emphasisPattern;
  749. emphasisPattern.lastIndex = 0;
  750. var result = [],
  751. match,
  752. previousLast = 0,
  753. savedLastIndex;
  754. while ((match = emphasisPattern.exec(text))) {
  755. var whole = match[0];
  756. var pre = match[1];
  757. var marker = match[2];
  758. var body = match[3];
  759. var post = match[4];
  760. {
  761. // parse links
  762. var matchBegin = emphasisPattern.lastIndex - whole.length;
  763. var beforeContent = text.substring(previousLast, matchBegin + pre.length);
  764. savedLastIndex = emphasisPattern.lastIndex;
  765. result.push(this.parseLink(beforeContent));
  766. emphasisPattern.lastIndex = savedLastIndex;
  767. }
  768. var bodyNode = [Node.createText(null, { value: body })];
  769. var bodyContainer = this.emphasizeElementByMarker(bodyNode, marker);
  770. result.push(bodyContainer);
  771. previousLast = emphasisPattern.lastIndex - post.length;
  772. }
  773. if (emphasisPattern.lastIndex === 0 ||
  774. emphasisPattern.lastIndex !== text.length - 1)
  775. result.push(this.parseLink(text.substring(previousLast)));
  776. if (result.length === 1) {
  777. // Avoid duplicated inline container wrapping
  778. return result[0];
  779. } else {
  780. return Node.createInlineContainer(result);
  781. }
  782. },
  783. depth: 0,
  784. parseLink: function (text) {
  785. var linkPattern = this.linkPattern;
  786. linkPattern.lastIndex = 0;
  787. var match,
  788. result = [],
  789. previousLast = 0,
  790. savedLastIndex;
  791. while ((match = linkPattern.exec(text))) {
  792. var whole = match[0];
  793. var src = match[1];
  794. var title = match[2];
  795. // parse before content
  796. var matchBegin = linkPattern.lastIndex - whole.length;
  797. var beforeContent = text.substring(previousLast, matchBegin);
  798. result.push(Node.createText(null, { value: beforeContent }));
  799. // parse link
  800. var link = Node.createLink([]);
  801. link.src = src;
  802. if (title) {
  803. savedLastIndex = linkPattern.lastIndex;
  804. link.appendChild(this.parseEmphasis(title));
  805. linkPattern.lastIndex = savedLastIndex;
  806. } else {
  807. link.appendChild(Node.createText(null, { value: src }));
  808. }
  809. result.push(link);
  810. previousLast = linkPattern.lastIndex;
  811. }
  812. if (linkPattern.lastIndex === 0 ||
  813. linkPattern.lastIndex !== text.length - 1)
  814. result.push(Node.createText(null, { value: text.substring(previousLast) }));
  815. return Node.createInlineContainer(result);
  816. },
  817. emphasizeElementByMarker: function (element, marker) {
  818. switch (marker) {
  819. case "*":
  820. return Node.createBold(element);
  821. case "/":
  822. return Node.createItalic(element);
  823. case "_":
  824. return Node.createUnderline(element);
  825. case "=":
  826. case "~":
  827. return Node.createCode(element);
  828. case "+":
  829. return Node.createDashed(element);
  830. }
  831. },
  832. buildEmphasisPattern: function () {
  833. return new RegExp(
  834. "([" + this.preEmphasis + "]|^|\r?\n)" + // \1 => pre
  835. "([" + this.markers + "])" + // \2 => marker
  836. "([^" + this.borderForbidden + "]|" + // \3 => body
  837. "[^" + this.borderForbidden + "]" +
  838. this.bodyRegexp +
  839. "[^" + this.borderForbidden + "])" +
  840. "\\2" +
  841. "([" + this.postEmphasis +"]|$|\r?\n)", // \4 => post
  842. // flags
  843. "g"
  844. );
  845. }
  846. };
  847. if (typeof exports !== "undefined") {
  848. exports.Parser = Parser;
  849. exports.InlineParser = InlineParser;
  850. }
  851. // var Node = require("../node.js").Node;
  852. function Converter() {
  853. }
  854. Converter.prototype = {
  855. exportOptions: {
  856. headerOffset: 1,
  857. exportFromLineNumber: false,
  858. suppressSubScriptHandling: false,
  859. suppressAutoLink: false
  860. },
  861. untitled: "Untitled",
  862. result: null,
  863. // TODO: Manage TODO lists
  864. initialize: function (orgDocument, exportOptions) {
  865. this.orgDocument = orgDocument;
  866. this.documentOptions = orgDocument.options || {};
  867. this.exportOptions = exportOptions || {};
  868. this.headers = [];
  869. this.headerOffset =
  870. typeof this.exportOptions.headerOffset === "number" ? this.exportOptions.headerOffset : 1;
  871. this.sectionNumbers = [0];
  872. },
  873. createTocItem: function (headerNode, parentTocs) {
  874. var childTocs = [];
  875. childTocs.parent = parentTocs;
  876. var tocItem = { headerNode: headerNode, childTocs: childTocs };
  877. return tocItem;
  878. },
  879. computeToc: function (exportTocLevel) {
  880. if (typeof exportTocLevel !== "number")
  881. exportTocLevel = Infinity;
  882. var toc = [];
  883. toc.parent = null;
  884. var previousLevel = 1;
  885. var currentTocs = toc; // first
  886. for (var i = 0; i < this.headers.length; ++i) {
  887. var headerNode = this.headers[i];
  888. if (headerNode.level > exportTocLevel)
  889. continue;
  890. var levelDiff = headerNode.level - previousLevel;
  891. if (levelDiff > 0) {
  892. for (var j = 0; j < levelDiff; ++j) {
  893. if (currentTocs.length === 0) {
  894. // Create a dummy tocItem
  895. var dummyHeader = Node.createHeader([], {
  896. level: previousLevel + j
  897. });
  898. dummyHeader.sectionNumberText = "";
  899. currentTocs.push(this.createTocItem(dummyHeader, currentTocs));
  900. }
  901. currentTocs = currentTocs[currentTocs.length - 1].childTocs;
  902. }
  903. } else if (levelDiff < 0) {
  904. levelDiff = -levelDiff;
  905. for (var k = 0; k < levelDiff; ++k) {
  906. currentTocs = currentTocs.parent;
  907. }
  908. }
  909. currentTocs.push(this.createTocItem(headerNode, currentTocs));
  910. previousLevel = headerNode.level;
  911. }
  912. return toc;
  913. },
  914. convertNode: function (node, recordHeader, insideCodeElement) {
  915. if (!insideCodeElement) {
  916. if (node.type === Node.types.directive) {
  917. if (node.directiveName === "example" ||
  918. node.directiveName === "src") {
  919. insideCodeElement = true;
  920. }
  921. } else if (node.type === Node.types.preformatted) {
  922. insideCodeElement = true;
  923. }
  924. }
  925. if (typeof node === "string") {
  926. node = Node.createText(null, { value: node });
  927. }
  928. var childText = node.children ? this.convertNodes(node.children, recordHeader, insideCodeElement) : "";
  929. var text;
  930. var auxData = this.computeAuxDataForNode(node);
  931. switch (node.type) {
  932. case Node.types.header:
  933. // Parse task status
  934. var taskStatus = null;
  935. if (childText.indexOf("TODO ") === 0)
  936. taskStatus = "todo";
  937. else if (childText.indexOf("DONE ") === 0)
  938. taskStatus = "done";
  939. // Compute section number
  940. var sectionNumberText = null;
  941. if (recordHeader) {
  942. var thisHeaderLevel = node.level;
  943. var previousHeaderLevel = this.sectionNumbers.length;
  944. if (thisHeaderLevel > previousHeaderLevel) {
  945. // Fill missing section number
  946. var levelDiff = thisHeaderLevel - previousHeaderLevel;
  947. for (var j = 0; j < levelDiff; ++j) {
  948. this.sectionNumbers[thisHeaderLevel - 1 - j] = 0; // Extend
  949. }
  950. } else if (thisHeaderLevel < previousHeaderLevel) {
  951. this.sectionNumbers.length = thisHeaderLevel; // Collapse
  952. }
  953. this.sectionNumbers[thisHeaderLevel - 1]++;
  954. sectionNumberText = this.sectionNumbers.join(".");
  955. node.sectionNumberText = sectionNumberText; // Can be used in ToC
  956. }
  957. text = this.convertHeader(node, childText, auxData,
  958. taskStatus, sectionNumberText);
  959. if (recordHeader)
  960. this.headers.push(node);
  961. break;
  962. case Node.types.orderedList:
  963. text = this.convertOrderedList(node, childText, auxData);
  964. break;
  965. case Node.types.unorderedList:
  966. text = this.convertUnorderedList(node, childText, auxData);
  967. break;
  968. case Node.types.definitionList:
  969. text = this.convertDefinitionList(node, childText, auxData);
  970. break;
  971. case Node.types.listElement:
  972. if (node.isDefinitionList) {
  973. var termText = this.convertNodes(node.term, recordHeader, insideCodeElement);
  974. text = this.convertDefinitionItem(node, childText, auxData,
  975. termText, childText);
  976. } else {
  977. text = this.convertListItem(node, childText, auxData);
  978. }
  979. break;
  980. case Node.types.paragraph:
  981. text = this.convertParagraph(node, childText, auxData);
  982. break;
  983. case Node.types.preformatted:
  984. text = this.convertPreformatted(node, childText, auxData);
  985. break;
  986. case Node.types.table:
  987. text = this.convertTable(node, childText, auxData);
  988. break;
  989. case Node.types.tableRow:
  990. text = this.convertTableRow(node, childText, auxData);
  991. break;
  992. case Node.types.tableCell:
  993. if (node.isHeader)
  994. text = this.convertTableHeader(node, childText, auxData);
  995. else
  996. text = this.convertTableCell(node, childText, auxData);
  997. break;
  998. case Node.types.horizontalRule:
  999. text = this.convertHorizontalRule(node, childText, auxData);
  1000. break;
  1001. // ============================================================ //
  1002. // Inline
  1003. // ============================================================ //
  1004. case Node.types.inlineContainer:
  1005. text = this.convertInlineContainer(node, childText, auxData);
  1006. break;
  1007. case Node.types.bold:
  1008. text = this.convertBold(node, childText, auxData);
  1009. break;
  1010. case Node.types.italic:
  1011. text = this.convertItalic(node, childText, auxData);
  1012. break;
  1013. case Node.types.underline:
  1014. text = this.convertUnderline(node, childText, auxData);
  1015. break;
  1016. case Node.types.code:
  1017. text = this.convertCode(node, childText, auxData);
  1018. break;
  1019. case Node.types.dashed:
  1020. text = this.convertDashed(node, childText, auxData);
  1021. break;
  1022. case Node.types.link:
  1023. text = this.convertLink(node, childText, auxData);
  1024. break;
  1025. case Node.types.directive:
  1026. switch (node.directiveName) {
  1027. case "quote":
  1028. text = this.convertQuote(node, childText, auxData);
  1029. break;
  1030. case "example":
  1031. text = this.convertExample(node, childText, auxData);
  1032. break;
  1033. case "src":
  1034. text = this.convertSrc(node, childText, auxData);
  1035. break;
  1036. default:
  1037. text = childText;
  1038. }
  1039. break;
  1040. case Node.types.text:
  1041. text = this.convertText(node.value, insideCodeElement);
  1042. break;
  1043. default:
  1044. throw "Unknown node type: " + node.type;
  1045. }
  1046. if (typeof this.postProcess === "function") {
  1047. text = this.postProcess(node, text, insideCodeElement);
  1048. }
  1049. return text;
  1050. },
  1051. convertText: function (text, insideCodeElement) {
  1052. var escapedText = this.escapeSpecialChars(text, insideCodeElement);
  1053. if (!this.exportOptions.suppressSubScriptHandling && !insideCodeElement) {
  1054. escapedText = this.makeSubscripts(escapedText, insideCodeElement);
  1055. }
  1056. if (!this.exportOptions.suppressAutoLink) {
  1057. escapedText = this.linkURL(escapedText);
  1058. }
  1059. return escapedText;
  1060. },
  1061. convertNodes: function (nodes, recordHeader, insideCodeElement) {
  1062. return nodes.map(function (node) {
  1063. return this.convertNode(node, recordHeader, insideCodeElement);
  1064. }, this).join("");
  1065. },
  1066. getNodeTextContent: function (node) {
  1067. if (node.type === Node.types.text)
  1068. return this.escapeSpecialChars(node.value);
  1069. else
  1070. return node.children ? node.children.map(this.getNodeTextContent, this).join("") : "";
  1071. },
  1072. // @Override
  1073. escapeSpecialChars: function (text) {
  1074. throw "Implement escapeSpecialChars";
  1075. },
  1076. // http://daringfireball.net/2010/07/improved_regex_for_matching_urls
  1077. urlPattern: /\b(?:https?:\/\/|www\d{0,3}[.]|[a-z0-9.\-]+[.][a-z]{2,4}\/)(?:[^\s()<>]+|\(([^\s()<>]+|(\([^\s()<>]+\)))*\))+(?:\(([^\s()<>]+|(\([^\s()<>]+\)))*\)|[^\s`!()\[\]{};:'".,<>?«»“”‘’])/i,
  1078. // @Override
  1079. linkURL: function (text) {
  1080. var self = this;
  1081. return text.replace(this.urlPattern, function (matched) {
  1082. if (matched.indexOf("://") < 0)
  1083. matched = "http://" + matched;
  1084. return self.makeLink(matched);
  1085. });
  1086. },
  1087. makeLink: function (url) {
  1088. throw "Implement makeLink";
  1089. },
  1090. makeSubscripts: function (text) {
  1091. if (this.documentOptions["^"] === "{}")
  1092. return text.replace(/\b([^_ \t]*)_{([^}]*)}/g,
  1093. this.makeSubscript);
  1094. else if (this.documentOptions["^"])
  1095. return text.replace(/\b([^_ \t]*)_([^_]*)\b/g,
  1096. this.makeSubscript);
  1097. else
  1098. return text;
  1099. },
  1100. makeSubscript: function (match, body, subscript) {
  1101. throw "Implement makeSubscript";
  1102. },
  1103. imageExtensionPattern: new RegExp("(" + [
  1104. "bmp", "png", "jpeg", "jpg", "gif", "tiff",
  1105. "tif", "xbm", "xpm", "pbm", "pgm", "ppm"
  1106. ].join("|") + ")$", "i")
  1107. };
  1108. if (typeof exports !== "undefined")
  1109. exports.Converter = Converter;
  1110. // var Converter = require("./converter.js").Converter;
  1111. // var Node = require("../node.js").Node;
  1112. function ConverterHTML(orgDocument, exportOptions) {
  1113. this.initialize(orgDocument, exportOptions);
  1114. this.result = this.convert();
  1115. }
  1116. ConverterHTML.prototype = {
  1117. __proto__: Converter.prototype,
  1118. convert: function () {
  1119. var title = this.orgDocument.title ? this.convertNode(this.orgDocument.title) : this.untitled;
  1120. var titleHTML = this.tag("h1", title);
  1121. var contentHTML = this.convertNodes(this.orgDocument.nodes, true /* record headers */);
  1122. var toc = this.computeToc(this.documentOptions["toc"]);
  1123. var tocHTML = this.tocToHTML(toc);
  1124. return {
  1125. title: title,
  1126. titleHTML: titleHTML,
  1127. contentHTML: contentHTML,
  1128. tocHTML: tocHTML,
  1129. toc: toc,
  1130. toString: function () {
  1131. return titleHTML + tocHTML + "\n" + contentHTML;
  1132. }
  1133. };
  1134. },
  1135. tocToHTML: function (toc) {
  1136. function tocToHTMLFunction(tocList) {
  1137. var html = "";
  1138. for (var i = 0; i < tocList.length; ++i) {
  1139. var tocItem = tocList[i];
  1140. var sectionNumberText = tocItem.headerNode.sectionNumberText;
  1141. var sectionNumber = this.documentOptions.num ?
  1142. this.inlineTag("span", sectionNumberText, {
  1143. "class": "section-number"
  1144. }) : "";
  1145. var header = this.getNodeTextContent(tocItem.headerNode);
  1146. var headerLink = this.inlineTag("a", sectionNumber + header, {
  1147. href: "#header-" + sectionNumberText.replace(/\./g, "-")
  1148. });
  1149. var subList = tocItem.childTocs.length ? tocToHTMLFunction.call(this, tocItem.childTocs) : "";
  1150. html += this.tag("li", headerLink + subList);
  1151. }
  1152. return this.tag("ul", html);
  1153. }
  1154. return tocToHTMLFunction.call(this, toc);
  1155. },
  1156. computeAuxDataForNode: function (node) {
  1157. while (node.parent &&
  1158. node.parent.type === Node.types.inlineContainer) {
  1159. node = node.parent;
  1160. }
  1161. var attributesNode = node.previousSibling;
  1162. var attributesText = "";
  1163. while (attributesNode &&
  1164. attributesNode.type === Node.types.directive &&
  1165. attributesNode.directiveName === "attr_html:") {
  1166. attributesText += attributesNode.directiveRawValue + " ";
  1167. attributesNode = attributesNode.previousSibling;
  1168. }
  1169. return attributesText;
  1170. },
  1171. // ----------------------------------------------------
  1172. // Node conversion
  1173. // ----------------------------------------------------
  1174. convertHeader: function (node, childText, auxData,
  1175. taskStatus, sectionNumberText) {
  1176. var headerAttributes = {};
  1177. if (taskStatus) {
  1178. childText = this.inlineTag("span", childText.substring(0, 4), {
  1179. "class": "task-status " + taskStatus
  1180. }) + childText.substring(5);
  1181. }
  1182. if (sectionNumberText) {
  1183. childText = this.inlineTag("span", sectionNumberText, {
  1184. "class": "section-number"
  1185. }) + childText;
  1186. headerAttributes["id"] = "header-" + sectionNumberText.replace(/\./g, "-");
  1187. }
  1188. if (taskStatus)
  1189. headerAttributes["class"] = "task-status " + taskStatus;
  1190. return this.tag("h" + (this.headerOffset + node.level),
  1191. childText, headerAttributes, auxData);
  1192. },
  1193. convertOrderedList: function (node, childText, auxData) {
  1194. return this.tag("ol", childText, null, auxData);
  1195. },
  1196. convertUnorderedList: function (node, childText, auxData) {
  1197. return this.tag("ul", childText, null, auxData);
  1198. },
  1199. convertDefinitionList: function (node, childText, auxData) {
  1200. return this.tag("dl", childText, null, auxData);
  1201. },
  1202. convertDefinitionItem: function (node, childText, auxData,
  1203. term, definition) {
  1204. return this.tag("dt", term) + this.tag("dd", definition);
  1205. },
  1206. convertListItem: function (node, childText, auxData) {
  1207. if (this.exportOptions.suppressCheckboxHandling) {
  1208. return this.tag("li", childText, null, auxData);
  1209. } else {
  1210. var listItemAttributes = {};
  1211. var listItemText = childText;
  1212. // Embed checkbox
  1213. if (/^\s*\[(X| |-)\]([\s\S]*)/.exec(listItemText)) {
  1214. listItemText = RegExp.$2 ;
  1215. var checkboxIndicator = RegExp.$1;
  1216. var checkboxAttributes = { type: "checkbox" };
  1217. switch (checkboxIndicator) {
  1218. case "X":
  1219. checkboxAttributes["checked"] = "true";
  1220. listItemAttributes["data-checkbox-status"] = "done";
  1221. break;
  1222. case "-":
  1223. listItemAttributes["data-checkbox-status"] = "intermediate";
  1224. break;
  1225. default:
  1226. listItemAttributes["data-checkbox-status"] = "undone";
  1227. break;
  1228. }
  1229. listItemText = this.inlineTag("input", null, checkboxAttributes) + listItemText;
  1230. }
  1231. return this.tag("li", listItemText, listItemAttributes, auxData);
  1232. }
  1233. },
  1234. convertParagraph: function (node, childText, auxData) {
  1235. return this.tag("p", childText, null, auxData);
  1236. },
  1237. convertPreformatted: function (node, childText, auxData) {
  1238. return this.tag("pre", childText, null, auxData);
  1239. },
  1240. convertTable: function (node, childText, auxData) {
  1241. return this.tag("table", this.tag("tbody", childText), null, auxData);
  1242. },
  1243. convertTableRow: function (node, childText, auxData) {
  1244. return this.tag("tr", childText);
  1245. },
  1246. convertTableHeader: function (node, childText, auxData) {
  1247. return this.tag("th", childText);
  1248. },
  1249. convertTableCell: function (node, childText, auxData) {
  1250. return this.tag("td", childText);
  1251. },
  1252. convertHorizontalRule: function (node, childText, auxData) {
  1253. return this.tag("hr", null, null, auxData);
  1254. },
  1255. convertInlineContainer: function (node, childText, auxData) {
  1256. return childText;
  1257. },
  1258. convertBold: function (node, childText, auxData) {
  1259. return this.inlineTag("b", childText);
  1260. },
  1261. convertItalic: function (node, childText, auxData) {
  1262. return this.inlineTag("i", childText);
  1263. },
  1264. convertUnderline: function (node, childText, auxData) {
  1265. return this.inlineTag("span", childText, {
  1266. style: "text-decoration:underline;"
  1267. });
  1268. },
  1269. convertCode: function (node, childText, auxData) {
  1270. return this.inlineTag("code", childText);
  1271. },
  1272. convertDashed: function (node, childText, auxData) {
  1273. return this.inlineTag("del", childText);
  1274. },
  1275. convertLink: function (node, childText, auxData) {
  1276. if (this.imageExtensionPattern.exec(node.src)) {
  1277. var imgText = this.getNodeTextContent(node);
  1278. return this.inlineTag("img", null, {
  1279. src: node.src,
  1280. alt: imgText,
  1281. title: imgText
  1282. }, auxData);
  1283. } else {
  1284. return this.inlineTag("a", childText, { href: node.src });
  1285. }
  1286. },
  1287. convertQuote: function (node, childText, auxData) {
  1288. return this.tag("blockquote", childText, null, auxData);
  1289. },
  1290. convertExample: function (node, childText, auxData) {
  1291. return this.tag("pre", childText, null, auxData);
  1292. },
  1293. convertSrc: function (node, childText, auxData) {
  1294. var codeLanguage = node.directiveArguments.length
  1295. ? node.directiveArguments[0]
  1296. : "unknown";
  1297. childText = this.tag("code", childText, {
  1298. "class": "language-" + codeLanguage
  1299. }, auxData);
  1300. return this.tag("pre", childText, {
  1301. "class": "prettyprint"
  1302. });
  1303. },
  1304. // ----------------------------------------------------
  1305. // Supplemental methods
  1306. // ----------------------------------------------------
  1307. replaceMap: {
  1308. // [replacing pattern, predicate]
  1309. "&": ["&#38;", null],
  1310. "<": ["&#60;", null],
  1311. ">": ["&#62;", null],
  1312. '"': ["&#34;", null],
  1313. "'": ["&#39;", null],
  1314. "->": ["&#10132;", function (text, insideCodeElement) {
  1315. return this.exportOptions.translateSymbolArrow && !insideCodeElement;
  1316. }]
  1317. },
  1318. replaceRegexp: null,
  1319. // @implement @override
  1320. escapeSpecialChars: function (text, insideCodeElement) {
  1321. if (!this.replaceRegexp) {
  1322. this.replaceRegexp = new RegExp(Object.keys(this.replaceMap).join("|"), "g");
  1323. }
  1324. var replaceMap = this.replaceMap;
  1325. var self = this;
  1326. return text.replace(this.replaceRegexp, function (matched) {
  1327. if (!replaceMap[matched]) {
  1328. throw "escapeSpecialChars: Invalid match";
  1329. }
  1330. var predicate = replaceMap[matched][1];
  1331. if (typeof predicate === "function" &&
  1332. !predicate.call(self, text, insideCodeElement)) {
  1333. // Not fullfill the predicate
  1334. return matched;
  1335. }
  1336. return replaceMap[matched][0];
  1337. });
  1338. },
  1339. // @implement
  1340. postProcess: function (node, currentText, insideCodeElement) {
  1341. if (this.exportOptions.exportFromLineNumber &&
  1342. typeof node.fromLineNumber === "number") {
  1343. // Wrap with line number information
  1344. currentText = this.inlineTag("div", currentText, {
  1345. "data-line-number": node.fromLineNumber
  1346. });
  1347. }
  1348. return currentText;
  1349. },
  1350. // @implement
  1351. makeLink: function (url) {
  1352. return "<a href=\"" + url + "\">" + decodeURIComponent(url) + "</a>";
  1353. },
  1354. // @implement
  1355. makeSubscript: function (match, body, subscript) {
  1356. return "<span class=\"org-subscript-parent\">" +
  1357. body +
  1358. "</span><span class=\"org-subscript-child\">" +
  1359. subscript +
  1360. "</span>";
  1361. },
  1362. // ----------------------------------------------------
  1363. // Specific methods
  1364. // ----------------------------------------------------
  1365. attributesObjectToString: function (attributesObject) {
  1366. var attributesString = "";
  1367. for (var attributeName in attributesObject) {
  1368. if (attributesObject.hasOwnProperty(attributeName)) {
  1369. attributesString += " " + attributeName + "=\"" + attributesObject[attributeName] + "\"";
  1370. }
  1371. }
  1372. return attributesString;
  1373. },
  1374. inlineTag: function (name, innerText, attributesObject, auxAttributesText) {
  1375. attributesObject = attributesObject || {};
  1376. var htmlString = "<" + name;
  1377. // TODO: check duplicated attributes
  1378. if (auxAttributesText)
  1379. htmlString += " " + auxAttributesText;
  1380. htmlString += this.attributesObjectToString(attributesObject);
  1381. if (innerText === null)
  1382. return htmlString + "/>";
  1383. htmlString += ">" + innerText + "</" + name + ">";
  1384. return htmlString;
  1385. },
  1386. tag: function (name, innerText, attributesObject, auxAttributesText) {
  1387. return this.inlineTag(name, innerText, attributesObject, auxAttributesText) + "\n";
  1388. }
  1389. };
  1390. if (typeof exports !== "undefined")
  1391. exports.ConverterHTML = ConverterHTML;
  1392. return exports;
  1393. })();