Skip to content

Commit

Permalink
Merge remote-tracking branch 'lepture/feature-footnotes'
Browse files Browse the repository at this point in the history
  • Loading branch information
chjj committed Feb 23, 2014
2 parents bcf206e + 18b4858 commit 646dbf5
Showing 1 changed file with 104 additions and 18 deletions.
122 changes: 104 additions & 18 deletions lib/marked.js
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ var block = {
blockquote: /^( *>[^\n]+(\n(?!def)[^\n]+)*\n*)+/,
list: /^( *)(bull) [\s\S]+?(?:hr|def|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
def: /^ *\[([^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
def: /^ *\[([^^\]]+)\]: *<?([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
footnote: noop,
table: noop,
paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
text: /^[^\n]+/
Expand Down Expand Up @@ -78,21 +79,39 @@ block.gfm = merge({}, block.normal, {
fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
paragraph: /^/
});

block.gfm.paragraph = replace(block.paragraph)
('(?!', '(?!'
+ block.gfm.fences.source.replace('\\1', '\\2') + '|'
+ block.list.source.replace('\\1', '\\3') + '|')
();

/**
* GFM + Tables Block Grammar
* Tables Block Grammar
*/

block.tables = merge({}, block.gfm, {
block.tables = {
nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
});
};

/**
* Footnotes Block Grammar
*/
block.footnotes = {
footnote: /^\[\^([^\]]+)\]: *([^\n]*(?:\n+|$)(?: {1,}[^\n]*(?:\n+|$))*)/
};
block.footnotes.normal = {
footnote: block.footnotes.footnote
};
block.footnotes.normal.paragraph = replace(block.paragraph)(
'))+)', '|' + block.footnotes.footnote.source + '))+)'
)();
block.footnotes.gfm = {
footnote: block.footnotes.footnote
};
block.footnotes.gfm.paragraph = replace(block.gfm.paragraph)(
'))+)', '|' + block.footnotes.footnote.source + '))+)'
)();

/**
* Block Lexer
Expand All @@ -101,15 +120,21 @@ block.tables = merge({}, block.gfm, {
function Lexer(options) {
this.tokens = [];
this.tokens.links = {};
this.tokens.footnotes = [];
this.options = options || marked.defaults;
this.rules = block.normal;

if (this.options.gfm) {
if (this.options.tables) {
this.rules = block.tables;
} else {
this.rules = block.gfm;
this.rules = block.gfm;
if (this.options.footnotes) {
this.rules = merge({}, this.rules, block.footnotes.gfm);
}
} else if (this.options.footnotes) {
this.rules = merge({}, this.rules, block.footnotes.normal);
}

if (this.options.tables) {
this.rules = merge({}, this.rules, block.tables);
}
}

Expand Down Expand Up @@ -149,6 +174,7 @@ Lexer.prototype.lex = function(src) {
Lexer.prototype.token = function(src, top, bq) {
var src = src.replace(/^ +$/gm, '')
, next
, key
, loose
, cap
, bull
Expand Down Expand Up @@ -375,6 +401,15 @@ Lexer.prototype.token = function(src, top, bq) {
continue;
}

// footnote
if (top && (cap = this.rules.footnote.exec(src))) {
src = src.substring(cap[0].length);
key = cap[1].slice(1).toLowerCase();
this.tokens.footnotes.push({key: key, text: cap[2]});
this.tokens.footnotes[key] = {text: cap[2], count: this.tokens.footnotes.length};
continue;
}

// table (gfm)
if (top && (cap = this.rules.table.exec(src))) {
src = src.substring(cap[0].length);
Expand Down Expand Up @@ -458,10 +493,11 @@ var inline = {
code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
br: /^ {2,}\n(?!\s*$)/,
del: noop,
footnote: noop,
text: /^[\s\S]+?(?=[\\<!\[_*`]| {2,}\n|$)/
};

inline._inside = /(?:\[[^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
inline._inside = /(?:\[[^^\]]*\]|[^\[\]]|\](?=[^\[]*\]))*/;
inline._href = /\s*<?([\s\S]*?)>?(?:\s+['"]([\s\S]*?)['"])?\s*/;

inline.link = replace(inline.link)
Expand All @@ -483,10 +519,10 @@ inline.normal = merge({}, inline);
* Pedantic Inline Grammar
*/

inline.pedantic = merge({}, inline.normal, {
inline.pedantic = {
strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
});
};

/**
* GFM Inline Grammar
Expand All @@ -511,13 +547,22 @@ inline.breaks = merge({}, inline.gfm, {
text: replace(inline.gfm.text)('{2,}', '*')()
});

/**
* Footnote Inline Grammar
*/

inline.footnote = {
footnote: /^\[\^([^\]]+)\]/
};

/**
* Inline Lexer & Compiler
*/

function InlineLexer(links, options) {
function InlineLexer(links, footnotes, options) {
this.options = options || marked.defaults;
this.links = links;
this.footnotes = footnotes || {};
this.rules = inline.normal;
this.renderer = this.options.renderer || new Renderer;
this.renderer.options = this.options;
Expand All @@ -533,8 +578,14 @@ function InlineLexer(links, options) {
} else {
this.rules = inline.gfm;
}
} else if (this.options.pedantic) {
this.rules = inline.pedantic;
}

if (this.options.footnotes) {
this.rules = merge({}, this.rules, inline.footnote);
}

if (this.options.pedantic) {
this.rules = merge({}, this.rules, inline.pedantic);
}
}

Expand All @@ -548,8 +599,8 @@ InlineLexer.rules = inline;
* Static Lexing/Compiling Method
*/

InlineLexer.output = function(src, links, options) {
var inline = new InlineLexer(links, options);
InlineLexer.output = function(src, links, footnotes, options) {
var inline = new InlineLexer(links, footnotes, options);
return inline.output(src);
};

Expand All @@ -560,6 +611,8 @@ InlineLexer.output = function(src, links, options) {
InlineLexer.prototype.output = function(src) {
var out = ''
, link
, key
, ret
, text
, href
, cap;
Expand Down Expand Up @@ -611,6 +664,16 @@ InlineLexer.prototype.output = function(src) {
continue;
}

// footnote
if (cap = this.rules.footnote.exec(src)) {
key = cap[1].toLowerCase();
if (ret = this.footnotes[key]) {
src = src.substring(cap[0].length);
out += this.renderer.footnoteref(key, ret.count);
continue;
}
}

// link
if (cap = this.rules.link.exec(src)) {
src = src.substring(cap[0].length);
Expand Down Expand Up @@ -889,6 +952,24 @@ Renderer.prototype.image = function(href, title, text) {
return out;
};

Renderer.prototype.footnoteref = function(key, count) {
var out = '<sup class="footnote-ref" id="fnref-' + escape(key) + '">';
out += '<a href="#fn-' + escape(key) + '">' + count + '</a></sup>';
return out;
};

Renderer.prototype.footnotes = function(notes) {
var out = '<ol class="footnotes">';
for (var i = 0; i < notes.length; i++) {
out += '<li id="fn-' + escape(notes[i].key) + '">';
out += notes[i].text;
out += '<a href="#fnref-' + escape(notes[i].key) + '">&#8617;</a>'
out += '</li>';
}
out += '</ol>';
return out;
};

/**
* Parsing & Compiling
*/
Expand Down Expand Up @@ -916,14 +997,18 @@ Parser.parse = function(src, options, renderer) {
*/

Parser.prototype.parse = function(src) {
this.inline = new InlineLexer(src.links, this.options, this.renderer);
this.inline = new InlineLexer(src.links, src.footnotes, this.options);
this.tokens = src.reverse();

var out = '';
while (this.next()) {
out += this.tok();
}

if (src.footnotes.length) {
out += this.renderer.footnotes(src.footnotes);
}

return out;
};

Expand Down Expand Up @@ -1223,6 +1308,7 @@ marked.setOptions = function(opt) {
marked.defaults = {
gfm: true,
tables: true,
footnotes: false,
breaks: false,
pedantic: false,
sanitize: false,
Expand Down

0 comments on commit 646dbf5

Please sign in to comment.