Comparing version 0.0.3 to 0.0.4
@@ -9,69 +9,2 @@ /** | ||
/** | ||
* Client-Side Shim | ||
*/ | ||
if (typeof module === 'undefined') { | ||
var keys = Object.keys | ||
, hop = Object.prototype.hasOwnProperty | ||
, first = true | ||
, a = Array.prototype; | ||
Object.keys = function(obj) { | ||
if (obj === inline) { | ||
if (!first) { | ||
return [ | ||
'autolink', | ||
'tag', | ||
'img', | ||
'link', | ||
'reflink', | ||
'strong', | ||
'em', | ||
'escape', | ||
'text' | ||
]; | ||
} | ||
first = false; | ||
} else if (obj === block) { | ||
return [ | ||
'newline', | ||
'block', | ||
'heading', | ||
'lheading', | ||
'hr', | ||
'blockquote', | ||
'list', | ||
'html', | ||
'text' | ||
]; | ||
} | ||
if (keys) { | ||
return keys.call(Object, obj); | ||
} | ||
var out = [] | ||
, key; | ||
for (key in obj) if (hop.call(obj, key)) { | ||
out.push(key); | ||
} | ||
return out; | ||
}; | ||
if (!a.forEach) a.forEach = function(func, context) { | ||
var i = 0 | ||
, l = this.length; | ||
for (; i < l; i++) { | ||
func.call(context, this[i], i, this); | ||
} | ||
}; | ||
if (!a.map) a.map = function(func, context) { | ||
var out = []; | ||
a.forEach.call(this, function(val, i, obj) { | ||
out.push(func.call(context, val, i, obj)); | ||
}); | ||
return out; | ||
}; | ||
} | ||
/** | ||
* Block-Level Grammar | ||
@@ -82,3 +15,3 @@ */ | ||
newline: /^\n/, | ||
block: /^[ ]{4,}[^\n]*(?:\n[ ]{4,}[^\n]*)*/, | ||
block: /^ {4,}[^\n]*(?:\n {4,}[^\n]*)*/, | ||
heading: /^ *(#{1,6}) *([^\n#]*) *#*/, | ||
@@ -93,3 +26,13 @@ lheading: /^([^\n]+)\n *(=|-){3,}/, | ||
block.keys = Object.keys(block); | ||
block.keys = [ | ||
'newline', | ||
'block', | ||
'heading', | ||
'lheading', | ||
'hr', | ||
'blockquote', | ||
'list', | ||
'html', | ||
'text' | ||
]; | ||
@@ -106,6 +49,5 @@ /** | ||
str = str.replace(/\r\n/g, '\n') | ||
.replace(/\r/g, '\n'); | ||
.replace(/\r/g, '\n') | ||
.replace(/\t/g, ' '); | ||
str = str.replace(/\t/g, ' '); | ||
// experimental | ||
@@ -116,3 +58,3 @@ //str = str.replace(/(^|\n) +(\n|$)/g, '$1$2'); | ||
str = str.replace( | ||
/^ {0,3}\[([^\]]+)\]: *([^ ]+)(?: +"([^"]+)")?[ \t]*(?:\n|$)/gm, | ||
/^ {0,3}\[([^\]]+)\]: *([^ ]+)(?: +"([^"]+)")? *(?:\n|$)/gm, | ||
function(_, id, href, title) { | ||
@@ -132,20 +74,21 @@ links[id] = { | ||
block.token = function lex(str, tokens, line) { | ||
var keys = block.keys | ||
, len = keys.length; | ||
var i | ||
block.token = function(str, tokens, line) { | ||
var rules = block | ||
, keys = block.keys | ||
, len = keys.length | ||
, key | ||
, rule | ||
, cap; | ||
while (str.length) | ||
for (i = 0; i < len; i++) { | ||
key = keys[i]; | ||
rule = block[key]; | ||
var scan = function() { | ||
if (!str) return; | ||
for (var i = 0; i < len; i++) { | ||
key = keys[i]; | ||
if (cap = rules[key].exec(str)) { | ||
str = str.substring(cap[0].length); | ||
return true; | ||
} | ||
} | ||
}; | ||
cap = rule.exec(str); | ||
if (!cap) continue; | ||
str = str.substring(cap[0].length); | ||
while (scan()) { | ||
switch (key) { | ||
@@ -188,3 +131,3 @@ case 'newline': | ||
type: 'list_start', | ||
ordered: isFinite(cap[2]), | ||
ordered: isFinite(cap[2]), | ||
line: line | ||
@@ -197,3 +140,3 @@ }); | ||
); | ||
cap.forEach(function(item) { | ||
each(cap, function(item) { | ||
// remove the list items sigil | ||
@@ -204,20 +147,14 @@ // so its seen as the next token | ||
// list item contains, hacky | ||
var len = /\n( +)/.exec(item); | ||
if (len) { | ||
len = len[1].length; | ||
item = item.replace( | ||
new RegExp('^ {' + len + '}', 'gm'), | ||
'' | ||
); | ||
var space = /\n( +)/.exec(item); | ||
if (space) { | ||
space = new RegExp('^' + space[1], 'gm'); | ||
item = item.replace(space, ''); | ||
} | ||
tokens.push({ | ||
type: 'list_item_start', | ||
type: 'list_item_start', | ||
line: line | ||
}); | ||
// recurse | ||
lex(item, tokens, line); | ||
block.token(item, tokens, line); | ||
tokens.push({ | ||
type: 'list_item_end', | ||
type: 'list_item_end', | ||
line: line | ||
@@ -227,3 +164,3 @@ }); | ||
tokens.push({ | ||
type: 'list_end', | ||
type: 'list_end', | ||
line: line | ||
@@ -242,10 +179,7 @@ }); | ||
tokens.push({ | ||
type: 'blockquote_start', | ||
type: 'blockquote_start', | ||
line: line | ||
}); | ||
cap = cap[0].replace(/^ *>/gm, ''); | ||
// recurse | ||
lex(cap, tokens, line); | ||
cap = cap[0].replace(/^ *>/gm, ''); | ||
block.token(cap, tokens, line); | ||
tokens.push({ | ||
@@ -257,3 +191,2 @@ type: 'blockquote_end', | ||
} | ||
break; | ||
} | ||
@@ -269,23 +202,44 @@ | ||
var inline = { | ||
autolink: /^<([^ >]*(:|@)[^ >]*)>/, | ||
escape: /^\\([\\`*{}\[\]()#+\-.!])/, | ||
autolink: /^<([^ >]+(:|@)[^ >]+)>/, | ||
tag: /^<[^\n>]+>/, | ||
img: /^!\[([^\]]+)\]\(([^\s\)]+)\s*([^\)]*)\)/, | ||
link: /^\[([^\]]+)\]\(([^\)]+)\)/, | ||
reflink: /^\[([^\]]+)\]\[([^\]]+)\]/, | ||
link: /^!?\[([^\]]+)\]\(([^\)]+)\)/, | ||
reflink: /^!?\[([^\]]+)\]\[([^\]]+)\]/, | ||
strong: /^__([\s\S]+?)__|^\*\*([\s\S]+?)\*\*/, | ||
em: /^_([^_]+)_|^\*([^*]+)\*/, | ||
escape: /^`([^`]+)`|^``([\s\S]+?)``/ | ||
code: /^`([^`]+)`|^``([\s\S]+?)``/ | ||
}; | ||
// super hack for performance | ||
inline.text = new RegExp( | ||
'^([\\s\\S]+?)(?=' | ||
+ Object.keys(inline).map(function(key) { | ||
return inline[key].source.replace(/(^|[^\[])\^/g, '$1'); | ||
}).join('|') | ||
+ '|$)' | ||
); | ||
inline.keys = [ | ||
'escape', | ||
'autolink', | ||
'tag', | ||
'link', | ||
'reflink', | ||
'strong', | ||
'em', | ||
'code' | ||
]; | ||
inline.keys = Object.keys(inline); | ||
// hacky, but performant | ||
inline.text = (function(rules) { | ||
var keys = rules.keys | ||
, i = 0 | ||
, l = keys.length | ||
, body = []; | ||
for (; i < l; i++) { | ||
body.push(rules[keys[i]].source | ||
.replace(/(^|[^\[])\^/g, '$1')); | ||
} | ||
keys.push('text'); | ||
return new RegExp( | ||
'^([\\s\\S]+?)(?=' | ||
+ body.join('|') | ||
+ '|$)' | ||
); | ||
})(inline); | ||
/** | ||
@@ -296,46 +250,44 @@ * Inline Lexer | ||
inline.lexer = function(str) { | ||
var out = '' | ||
, links = tokens.links | ||
, link = {} | ||
, text | ||
, href; | ||
var rules = inline | ||
, keys = inline.keys | ||
, len = keys.length | ||
, out = '' | ||
, links = tokens.links; | ||
var i | ||
, key | ||
, rule | ||
, cap | ||
, link | ||
, text | ||
, href; | ||
, cap; | ||
while (str.length) { | ||
for (i = 0; i < len; i++) { | ||
var scan = function() { | ||
if (!str) return; | ||
for (var i = 0; i < len; i++) { | ||
key = keys[i]; | ||
rule = rules[key]; | ||
if (cap = rules[key].exec(str)) { | ||
str = str.substring(cap[0].length); | ||
return true; | ||
} | ||
} | ||
}; | ||
cap = rule.exec(str); | ||
if (!cap) continue; | ||
str = str.substring(cap[0].length); | ||
switch (key) { | ||
case 'tag': | ||
out += cap[0]; | ||
break; | ||
case 'img': | ||
out += '<img src="' | ||
+ escape(cap[2]) | ||
+ '" alt="' + escape(cap[1]) | ||
+ '"' | ||
+ (cap[3] | ||
? ' title="' | ||
+ escape(cap[3]) | ||
+ '"' | ||
: '') | ||
+ '>'; | ||
break; | ||
case 'link': | ||
case 'reflink': | ||
link = links[cap[2]] || ''; | ||
while (scan()) { | ||
switch (key) { | ||
case 'escape': | ||
out += cap[1]; | ||
break; | ||
case 'tag': | ||
out += cap[0]; | ||
break; | ||
case 'link': | ||
case 'reflink': | ||
if (cap[0][0] !== '!') { | ||
if (key === 'reflink') { | ||
link = links[cap[2]]; | ||
} else { | ||
link.href = cap[2]; | ||
link.title = cap[3]; | ||
} | ||
out += '<a href="' | ||
+ escape(link.href || cap[2]) | ||
+ escape(link.href) | ||
+ '"' | ||
@@ -350,37 +302,55 @@ + (link.title | ||
+ '</a>'; | ||
break; | ||
case 'autolink': | ||
if (cap[2] === '@') { | ||
text = mangle(cap[1]); | ||
href = mangle('mailto:') + text; | ||
} else { | ||
if (key === 'reflink') { | ||
link = links[cap[2]]; | ||
} else { | ||
text = escape(cap[1]); | ||
href = text; | ||
text = /^([^\s]+)\s*(.+)?$/.exec(cap[2]); | ||
link.href = text[1]; | ||
link.title = text[2]; | ||
} | ||
out += '<a href="' + href + '">' | ||
+ text | ||
+ '</a>'; | ||
break; | ||
case 'strong': | ||
out += '<strong>' | ||
+ inline.lexer(cap[2] || cap[1]) | ||
+ '</strong>'; | ||
break; | ||
case 'em': | ||
out += '<em>' | ||
+ inline.lexer(cap[2] || cap[1]) | ||
+ '</em>'; | ||
break; | ||
case 'escape': | ||
out += '<code>' | ||
+ escape(cap[2] || cap[1]) | ||
+ '</code>'; | ||
break; | ||
case 'text': | ||
out += escape(cap[1]); | ||
break; | ||
default: | ||
break; | ||
} | ||
break; | ||
out += '<img src="' | ||
+ escape(link.href) | ||
+ '" alt="' | ||
+ escape(cap[1]) | ||
+ '"' | ||
+ (link.title | ||
? ' title="' | ||
+ escape(link.title) | ||
+ '"' | ||
: '') | ||
+ '>'; | ||
} | ||
break; | ||
case 'autolink': | ||
if (cap[2] === '@') { | ||
text = mangle(cap[1]); | ||
href = mangle('mailto:') + text; | ||
} else { | ||
text = escape(cap[1]); | ||
href = text; | ||
} | ||
out += '<a href="' + href + '">' | ||
+ text | ||
+ '</a>'; | ||
break; | ||
case 'strong': | ||
out += '<strong>' | ||
+ inline.lexer(cap[2] || cap[1]) | ||
+ '</strong>'; | ||
break; | ||
case 'em': | ||
out += '<em>' | ||
+ inline.lexer(cap[2] || cap[1]) | ||
+ '</em>'; | ||
break; | ||
case 'code': | ||
out += '<code>' | ||
+ escape(cap[2] || cap[1]) | ||
+ '</code>'; | ||
break; | ||
case 'text': | ||
out += escape(cap[1]); | ||
break; | ||
default: | ||
break; | ||
} | ||
@@ -437,7 +407,5 @@ } | ||
// list items here | ||
if (token.type === 'text') { | ||
body.push(inline.lexer(token.text)); | ||
} else { | ||
body.push(tok()); | ||
} | ||
body.push(token.type === 'text' | ||
? inline.lexer(token.text) | ||
: tok()); | ||
} | ||
@@ -450,11 +418,10 @@ return '<li>' | ||
case 'text': | ||
var body = [] | ||
, last = token.line; | ||
while (token && token.type === 'text') { | ||
if (token.line > last) break; | ||
last = token.line + 1; | ||
body.push(token.text); | ||
next(); | ||
var body = [ token.text ] | ||
, last = token.line | ||
, top; | ||
while ((top = tokens[tokens.length-1]) | ||
&& top.type === 'text' | ||
&& top.line === ++last) { | ||
body.push(next().text); | ||
} | ||
if (token) tokens.push(token); | ||
return '<p>' | ||
@@ -494,6 +461,6 @@ + inline.lexer(body.join(' ')) | ||
var mangle = function(str) { | ||
var ch | ||
var out = '' | ||
, ch | ||
, i = 0 | ||
, l = str.length | ||
, out = ''; | ||
, l = str.length; | ||
@@ -511,2 +478,7 @@ for (; i < l; i++) { | ||
var each = function(obj, func) { | ||
var i = 0, l = obj.length; | ||
for (; i < l; i++) func(obj[i]); | ||
}; | ||
/** | ||
@@ -513,0 +485,0 @@ * Expose |
@@ -5,3 +5,3 @@ { | ||
"author": "Christopher Jeffrey", | ||
"version": "0.0.3", | ||
"version": "0.0.4", | ||
"main": "./lib/marked.js", | ||
@@ -8,0 +8,0 @@ "bin": { "marked": "./bin/marked" }, |
# marked | ||
A full-featured markdown parser and compiler implemented in ~370 lines of JS. | ||
A full-featured markdown parser and compiler implemented in ~430 lines of JS. | ||
Built for speed. | ||
@@ -19,3 +19,4 @@ | ||
marked lingers around 350 lines long and still implements all markdown features. | ||
marked lingers around 430 (may vary) lines long and still implements all | ||
markdown features. It is also now fully compatible with the client-side. | ||
@@ -48,15 +49,16 @@ ## Install | ||
- Implement GFM features. | ||
- Possibly add some | ||
[ReMarkable](http://camendesign.com/code/remarkable/documentation.html) | ||
features while remaining backwardly compatible with all markdown syntax. | ||
- Optimize the parser so it accepts a stream of tokens from the lexer. This | ||
should enhance performance even further, although, no lookaheads would | ||
be possible. | ||
- Possibly add some | ||
[ReMarkable](http://camendesign.com/code/remarkable/documentation.html) | ||
features while remaining backwardly compatible with all markdown syntax. | ||
- Find a better way of testing. Create a test suite from scratch because most | ||
markdown compilers don't appear to be working properly in every aspect (but | ||
it's hard to tell because the markdown "spec" is so vague). | ||
- Possibly alter rules to recognize arbitrary blocks of HTML better. | ||
- Recognize and parse paragraph list items better. | ||
- Recognize and parse paragraph list items better. This may be the most | ||
important task. Currently, list items are parsed strictly, it's not possible | ||
to have "loose list items" as they're known. | ||
- Add an explicit pretty printing and minification feature. | ||
I've still just begun to write this. I expect I will be updating it frequently. |
@@ -16,3 +16,19 @@ var fs = require('fs') | ||
var showdown_ = require('showdown'); | ||
/** | ||
* There's two ways to benchmark showdown here. | ||
* The first way is to create a new converter | ||
* every time, this will renew any closured | ||
* variables. It is the "proper" way of using | ||
* showdown. However, for this benchmark, | ||
* I will use the completely improper method | ||
* which is must faster, just to be fair. | ||
*/ | ||
var showdown_ = (function() { | ||
var Showdown = require('showdown').Showdown; | ||
var convert = new Showdown.converter(); | ||
return function(str) { | ||
return convert.makeHtml(str); | ||
}; | ||
})(); | ||
benchmark(function showdown() { | ||
@@ -22,5 +38,5 @@ showdown_(text); | ||
var markdown_ = require('markdown'); | ||
var markdownjs_ = require('markdown-js'); | ||
benchmark(function markdownjs() { | ||
markdown_.toHTML(text); | ||
markdownjs_.toHTML(text); | ||
}); |
Sorry, the diff of this file is not supported yet
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
License Policy Violation
LicenseThis package is not allowed per your license policy. Review the package's license to ensure compliance.
Found 1 instance in 1 package
17615
13
63
488