From 3b785a0bfadf4ea1201d83fe40ab107f312fd77a Mon Sep 17 00:00:00 2001 From: Alex Kocharin Date: Wed, 29 Oct 2014 05:00:05 +0300 Subject: [PATCH] Add subscript and superscript --- lib/parser_inline.js | 4 ++- lib/renderer.js | 14 +++++++++ lib/rules_inline/sub.js | 68 +++++++++++++++++++++++++++++++++++++++++ lib/rules_inline/sup.js | 68 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 153 insertions(+), 1 deletion(-) create mode 100644 lib/rules_inline/sub.js create mode 100644 lib/rules_inline/sup.js diff --git a/lib/parser_inline.js b/lib/parser_inline.js index 6c03305..889cb62 100644 --- a/lib/parser_inline.js +++ b/lib/parser_inline.js @@ -19,6 +19,8 @@ _rules.push(require('./rules_inline/del')); _rules.push(require('./rules_inline/ins')); _rules.push(require('./rules_inline/mark')); _rules.push(require('./rules_inline/emphasis')); +_rules.push(require('./rules_inline/sub')); +_rules.push(require('./rules_inline/sup')); _rules.push(require('./rules_inline/links')); _rules.push(require('./rules_inline/autolink')); _rules.push(require('./rules_inline/htmltag')); @@ -49,7 +51,7 @@ function ParserInline() { // Rule to skip pure text // - '{}$%@+=:' reserved for extentions - this.textMatch = /[\n\\`*_\[\]!&<{}$%@~+=:]/; + this.textMatch = /[\n\\`*_^\[\]!&<{}$%@~+=:]/; // By default CommonMark allows too much in links // If you need to restrict it - override this with your validator. diff --git a/lib/renderer.js b/lib/renderer.js index 078b45c..3e742fe 100644 --- a/lib/renderer.js +++ b/lib/renderer.js @@ -240,6 +240,20 @@ rules.mark_close = function(/*tokens, idx, options*/) { }; +rules.sub_open = function(/*tokens, idx, options*/) { + return ''; +}; +rules.sub_close = function(/*tokens, idx, options*/) { + return ''; +}; +rules.sup_open = function(/*tokens, idx, options*/) { + return ''; +}; +rules.sup_close = function(/*tokens, idx, options*/) { + return ''; +}; + + rules.hardbreak = function (tokens, idx, options) { return options.xhtmlOut ? '
\n' : '
\n'; }; diff --git a/lib/rules_inline/sub.js b/lib/rules_inline/sub.js new file mode 100644 index 0000000..f89eb7b --- /dev/null +++ b/lib/rules_inline/sub.js @@ -0,0 +1,68 @@ +// Process ~subscript~ + +'use strict'; + +module.exports = function sub(state, silent) { + var found, + stack, + max = state.posMax, + start = state.pos, + lastChar, + nextChar; + + if (state.src.charCodeAt(start) !== 0x7E/* ~ */) { return false; } + if (start + 2 >= max) { return false; } + if (state.level >= state.options.maxNesting) { return false; } + + lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; + nextChar = state.src.charCodeAt(start + 2); + + if (nextChar === 0x20 || nextChar === 0x0A) { return false; } + + state.pos = start + 1; + stack = 1; + + while (state.pos < max) { + if (state.src.charCodeAt(state.pos) === 0x7E/* ~ */) { + lastChar = state.src.charCodeAt(state.pos - 1); + nextChar = state.pos + 1 < max ? state.src.charCodeAt(state.pos + 1) : -1; + if (nextChar !== 0x7E/* ~ */ && lastChar !== 0x7E/* ~ */) { + if (lastChar !== 0x20 && lastChar !== 0x0A) { + // closing '~' + stack--; + } else if (nextChar !== 0x20 && nextChar !== 0x0A) { + // opening '~' + stack++; + } // else { + // // standalone ' ~ ' indented with spaces + //} + if (stack <= 0) { + found = true; + break; + } + } + } + + state.parser.skipToken(state); + } + + if (!found) { + // parser failed to find ending tag, so it's not valid emphasis + state.pos = start; + return false; + } + + // found! + state.posMax = state.pos; + state.pos = start + 1; + + if (!silent) { + state.push({ type: 'sub_open', level: state.level++ }); + state.parser.tokenize(state); + state.push({ type: 'sub_close', level: --state.level }); + } + + state.pos = state.posMax + 1; + state.posMax = max; + return true; +}; diff --git a/lib/rules_inline/sup.js b/lib/rules_inline/sup.js new file mode 100644 index 0000000..e78a431 --- /dev/null +++ b/lib/rules_inline/sup.js @@ -0,0 +1,68 @@ +// Process ~superscript~ + +'use strict'; + +module.exports = function sup(state, silent) { + var found, + stack, + max = state.posMax, + start = state.pos, + lastChar, + nextChar; + + if (state.src.charCodeAt(start) !== 0x5E/* ^ */) { return false; } + if (start + 2 >= max) { return false; } + if (state.level >= state.options.maxNesting) { return false; } + + lastChar = start > 0 ? state.src.charCodeAt(start - 1) : -1; + nextChar = state.src.charCodeAt(start + 2); + + if (nextChar === 0x20 || nextChar === 0x0A) { return false; } + + state.pos = start + 1; + stack = 1; + + while (state.pos < max) { + if (state.src.charCodeAt(state.pos) === 0x5E/* ^ */) { + lastChar = state.src.charCodeAt(state.pos - 1); + nextChar = state.pos + 1 < max ? state.src.charCodeAt(state.pos + 1) : -1; + if (nextChar !== 0x5E/* ^ */ && lastChar !== 0x5E/* ^ */) { + if (lastChar !== 0x20 && lastChar !== 0x0A) { + // closing '^' + stack--; + } else if (nextChar !== 0x20 && nextChar !== 0x0A) { + // opening '^' + stack++; + } // else { + // // standalone ' ^ ' indented with spaces + //} + if (stack <= 0) { + found = true; + break; + } + } + } + + state.parser.skipToken(state); + } + + if (!found) { + // parser failed to find ending tag, so it's not valid emphasis + state.pos = start; + return false; + } + + // found! + state.posMax = state.pos; + state.pos = start + 1; + + if (!silent) { + state.push({ type: 'sub_open', level: state.level++ }); + state.parser.tokenize(state); + state.push({ type: 'sub_close', level: --state.level }); + } + + state.pos = state.posMax + 1; + state.posMax = max; + return true; +};