Comparing version 0.1.4 to 0.1.6
@@ -7,24 +7,47 @@ #!/usr/bin/env node | ||
, markdown = require('markdown').markdown | ||
, fullpath = process.argv[2] | ||
, nopt = require("nopt") | ||
, stream | ||
, opts | ||
, buffer = "" | ||
; | ||
function convert(err, data) { | ||
var md | ||
, html | ||
; | ||
if (err) { | ||
throw err; | ||
opts = nopt( | ||
{ "dialect": [ "Gruber", "Maruku"] | ||
, "help": Boolean | ||
} | ||
); | ||
md = data.toString('utf8'); | ||
html = markdown.toHTML(md); | ||
if (opts.help) { | ||
var name = process.argv[1].split('/').pop() | ||
console.warn( require('util').format( | ||
'usage: %s [--dialect=DIALECT] FILE\n\nValid dialects are Gruber (the default) or Maruku', | ||
name | ||
) ); | ||
process.exit(0); | ||
} | ||
if (!fullpath) { | ||
console.error('try: ', process.argv[1].split('/').pop(), '/path/to/doc.md'); | ||
return; | ||
var fullpath = opts.argv.remain[0]; | ||
if (fullpath && fullpath !== "-") { | ||
stream = fs.createReadStream(fullpath); | ||
} else { | ||
stream = process.stdin; | ||
} | ||
stream.resume(); | ||
stream.setEncoding('utf8'); | ||
fs.readFile(fullpath, convert); | ||
stream.on('error', function(error) { | ||
console.error(error.toString()); | ||
process.exit(1); | ||
}); | ||
stream.on('data', function(data) { | ||
buffer += data; | ||
}); | ||
stream.on('end', function() { | ||
var html = markdown.toHTML(buffer, opts.dialect); | ||
console.log(html); | ||
}); | ||
}()) |
@@ -5,3 +5,3 @@ var fs = require('fs'); | ||
if (!err) { | ||
var output = require("../lib/index").toHTML(data, {highlight: true } ); | ||
var output = require("../lib/index").toHTML(data, {highlight: true, smartyPants: true } ); | ||
fs.writeFileSync("SYNTAX.html", output.html); | ||
@@ -8,0 +8,0 @@ |
@@ -7,3 +7,3 @@ Author: A noble spirit | ||
Here's a list of current syntax options. Notations are provided for PHP Extras syntax, Maruku syntax, GFM, and NAMP dialects. | ||
Here's a list of current syntax options. Notations are provided for PHP Extras syntax, Maruku syntax, GFM, and NAMP dialects. For most of these, you'll want to also check out the HTML source to see what's going on. | ||
@@ -31,8 +31,8 @@ * * * | ||
Inline (titles are optional): | ||
![alt text](/path/img.jpg "Obviously, no image") | ||
![alt text](http://gfhiryuu.tripod.com/random/green_dragon_sprite.gif "Random image") | ||
Reference-style: | ||
![alt text][id] | ||
![alt text][id2] | ||
[id]: /url/to/img.jpg "Obviously, no image" | ||
[id2]: http://upload.wikimedia.org/wikipedia/en/math/b/8/b/b8b4326ebb88870f8cc97ab3f59a0867.png "Still, a random image" | ||
@@ -60,5 +60,19 @@ ## HEADERS | ||
1. Foo | ||
2. Bar | ||
1. Eggs | ||
2. Milk | ||
4. Cheese | ||
3. Anything | ||
Ordered, with paragraphs: | ||
1. Eggs | ||
Preferrably brown | ||
2. Milk | ||
Wait, I am lactose intolerant | ||
5. Cheese | ||
2. Wait, Cheese? | ||
Unordered, with paragraphs: | ||
@@ -79,3 +93,3 @@ | ||
2. Gorilla snack | ||
* (or monkeys) | ||
* (or, for monkeys) | ||
3. Yellow | ||
@@ -89,9 +103,9 @@ * Camel | ||
> > And, they can be nested. | ||
> > And, they can be nested. | ||
> #### Headers in blockquotes | ||
> | ||
> * You can quote a list. | ||
> * Etc. | ||
> * Like this. | ||
> 1. And nest one. | ||
@@ -117,3 +131,3 @@ | ||
Code tags can also be "fenced" by ` ``` ` (GitHub-Flavored-Markdown) | ||
Code blocks can also be "fenced" by ` ``` ` (GitHub-Flavored-Markdown) | ||
@@ -132,5 +146,5 @@ ``` | ||
(Highlighting is enabled by default; you'll have to define your own CSS.) | ||
(Highlighting is enabled by default; you'll have to define your own CSS that matches the highlight-js notation, though, which this document does not do!) | ||
## Strikethrough | ||
## STRIKETHROUGH | ||
@@ -159,2 +173,13 @@ Hey, this is ~~terrible~~ great! | ||
## PUNCTUATION | ||
"check it out" | ||
" | ||
\" | ||
`"should not curl"` | ||
*"Wow"* | ||
## TABLES (PHP Extras) | ||
@@ -164,3 +189,3 @@ ----------------- | ||
|a |b |c | ||
|--|--|-- | ||
|:---|---|--- | ||
|1 |2 |3 | ||
@@ -171,6 +196,6 @@ | ||
|:--|:--:|:-- | ||
|1 |2 |3 | ||
|1 |2 (or _two_) |[3]{: .threeClass} | ||
|4 |5 | 6 | ||
{: #demo test="true"} | ||
or | ||
@@ -180,37 +205,47 @@ x |y |z | ||
8 |9 |10 | ||
{: .table .table-striped .table-bordered .table-condensed} | ||
alignment | ||
Demonstrating alignment: | ||
right|left | center | ||
-----:|:-----|:------: | ||
0001 | 2 | 003 | ||
0001 | 2 (or _two_) | 003 | ||
4 | 0005 | 6 | ||
## DEFINITION LISTS (PHP Extras) | ||
(sadly, broken) | ||
----------------- | ||
Apple | ||
: Pomaceous fruit of plants of the genus Malus in | ||
the family Rosaceae. | ||
: Pomaceous fruit of plants of the genus Malus in the family Rosaceae. | ||
* List in dl | ||
* Another **item** | ||
* Wait, one more ! | ||
{: .listStyle} | ||
: Okay, back to another dd. | ||
Orange | ||
: The fruit of an evergreen tree of the genus Citrus. | ||
```python | ||
print "Testing yet again" | ||
``` | ||
It's extremly hard to come up with make believe text. | ||
Pomegranate | ||
: A deciduous shrub or small tree (Punica granatum), | ||
native to Asia and _widely cultivated_ for its edible fruit. Anyway, | ||
Blah blah blah.adsadsasd | ||
: A deciduous shrub or small tree (Punica granatum), native to Asia and _widely cultivated_ for its edible fruit. | ||
Anyway, Blah blah blah.adsadsasd. | ||
Term 1 | ||
: This is a definition with two paragraphs. Lorem ipsum | ||
dolor sit amet, consectetuer adipiscing elit. Aliquam | ||
hendrerit mi posuere lectus. | ||
: This is a definition with two paragraphs. | ||
Lorem ipsum dolor sit amet, consectetuer adipiscing elit. | ||
Aliquam hendrerit mi posuere lectus. | ||
: Second definition for term 1, also wrapped in a paragraph | ||
because of the blank line preceding it. | ||
because of the blank line preceding it. {: .test} | ||
{: #TestingIDHere} | ||
## ATTRIBUTES (Maruku) {: #myid .myclass} | ||
## ATTRIBUTES (Maruku) | ||
{: #myid .myclass} | ||
----------------- | ||
@@ -221,3 +256,2 @@ | ||
attributes! | ||
{: #bigBlock tutorial=yes} | ||
@@ -236,5 +270,5 @@ | ||
& < "aaa" | ||
& < "aaa" & | ||
## Boostrap tags | ||
## Twitter Bootstrap tags | ||
@@ -250,6 +284,7 @@ Note: This should be wrapped in a div that looks like this: `<div class='alert alert-success'>`. | ||
## INLINE HTML | ||
(sadly, broken) | ||
----------------- | ||
<p> | ||
HTML is represented as is.<br> | ||
HTML _should_ be represented as is.<br> | ||
<del>The <strong>quick brown fox</strong> jumps over the lazy dog.</del> | ||
@@ -259,3 +294,3 @@ </p> | ||
<div> | ||
Regularly Markdown syntax ignored in HTML.<br/> | ||
Regularly Markdown syntax ignored in HTML. | ||
[Google](http://www.google.co.jp/) | ||
@@ -262,0 +297,0 @@ </div> |
@@ -12,2 +12,3 @@ // Released under MIT license | ||
var highlightEnabled = true; // always highlight by default | ||
var smartyPantsEnabled = false; // support Smarty Pants translations | ||
var documentMetaExists = false; // whether or not there's a document meta block | ||
@@ -65,3 +66,3 @@ | ||
this.debug_indent = ""; | ||
} | ||
}; | ||
@@ -87,3 +88,3 @@ /** | ||
return md.toTree( source ); | ||
} | ||
}; | ||
@@ -109,2 +110,4 @@ /** | ||
highlightEnabled = false; | ||
if (options.smartyPants === true) | ||
smartyPantsEnabled = true; | ||
} | ||
@@ -150,4 +153,28 @@ | ||
return html; | ||
}; | ||
// For Spidermonkey based engines | ||
function mk_block_toSource() { | ||
return "Markdown.mk_block( " + | ||
uneval(this.toString()) + | ||
", " + | ||
uneval(this.trailing) + | ||
", " + | ||
uneval(this.lineNumber) + | ||
" )"; | ||
} | ||
// node | ||
function mk_block_inspect() { | ||
var util = require('util'); | ||
return "Markdown.mk_block( " + | ||
util.inspect(this.toString()) + | ||
", " + | ||
util.inspect(this.trailing) + | ||
", " + | ||
util.inspect(this.lineNumber) + | ||
" )"; | ||
} | ||
var mk_block = Markdown.mk_block = function(block, trail, line) { | ||
@@ -160,11 +187,4 @@ // Be helpful for default case in tests. | ||
// To make it clear its not just a string | ||
s.toSource = function() { | ||
return "Markdown.mk_block( " + | ||
uneval(block) + | ||
", " + | ||
uneval(trail) + | ||
", " + | ||
uneval(line) + | ||
" )" | ||
} | ||
s.inspect = mk_block_inspect; | ||
s.toSource = mk_block_toSource; | ||
@@ -175,2 +195,8 @@ if (line != undefined) | ||
return s; | ||
}; | ||
function count_lines( str ) { | ||
var n = 0, i = -1; | ||
while ( ( i = str.indexOf('\n', i+1) ) !== -1) n++; | ||
return n; | ||
} | ||
@@ -193,3 +219,3 @@ | ||
while ( ( m = re.exec(input) ) != null ) { | ||
while ( ( m = re.exec(input) ) !== null ) { | ||
blocks.push( mk_block( m[1], m[2], line_no ) ); | ||
@@ -200,3 +226,3 @@ line_no += helpers.count_lines( m[0] ); | ||
return blocks; | ||
} | ||
}; | ||
@@ -246,3 +272,3 @@ /** | ||
return []; | ||
} | ||
}; | ||
@@ -253,3 +279,3 @@ Markdown.prototype.processInline = function processInline( block ) { | ||
return this.dialect.inline.__call__.call( this, String( block ) ); | ||
} | ||
}; | ||
@@ -264,5 +290,3 @@ /** | ||
Markdown.prototype.toTree = function toTree( source, custom_root ) { | ||
var blocks = source instanceof Array | ||
? source | ||
: this.split_blocks( source ); | ||
var blocks = source instanceof Array ? source : this.split_blocks( source ); | ||
@@ -286,7 +310,7 @@ // Make tree a member variable so its easier to mess with in extensions | ||
finally { | ||
if ( custom_root ) | ||
if ( custom_root ) { | ||
this.tree = old_tree; | ||
} | ||
} | ||
}; | ||
@@ -313,53 +337,11 @@ // Noop by default | ||
return b; | ||
} | ||
}; | ||
Markdown.prototype.link = function( text ) { | ||
// [link text](/path/to/img.jpg "Optional title") | ||
// 1 2 3 4 <--- captures | ||
var m = text.match( /^\[([\s\S]*?)\][ \t]*\([ \t]*(\S+)(?:[ \t]+(["'])(.*?)\3)?[ \t]*\)/ ); | ||
/** | ||
* Markdown.dialects | ||
* | ||
* Namespace of built-in dialects. | ||
**/ | ||
Markdown.dialects = {}; | ||
if ( m ) { | ||
if ( m[2] && m[2][0] == '<' && m[2][m[2].length-1] == '>' ) | ||
m[2] = m[2].substring( 1, m[2].length - 1 ); | ||
// Process escapes only | ||
m[2] = Markdown.dialects.Gruber.inline.__call__.call( this, m[2], /\\/ )[0]; | ||
var attrs = { href: m[2] || "" }; | ||
if ( m[4] !== undefined) | ||
attrs.title = m[4]; | ||
var link = [ "link", attrs ]; | ||
Array.prototype.push.apply( link, Markdown.prototype.processInline( m[1] ) ); | ||
return [ m[0].length, link ]; | ||
} | ||
// [Alt text][id] | ||
// [Alt text] [id] | ||
// [id] | ||
m = text.match( /^\[[^~*_`]([\s\S]*?)\](?: ?\[(.*?)\])?/ ); | ||
if ( m ) { | ||
// [id] case, text == id | ||
if ( m[2] === undefined || m[2] === "" ) m[2] = m[1]; | ||
attrs = { ref: m[ 2 ].toLowerCase(), original: m[ 0 ] }; | ||
link = [ "link_ref", attrs ]; | ||
Array.prototype.push.apply( link, Markdown.prototype.processInline( m[1] ) ); | ||
// We can't check if the reference is known here as it likely wont be | ||
// found till after. Check it in md tree->html tree conversion. | ||
// Store the original so that conversion can revert if the ref isn't found. | ||
return [ | ||
m[ 0 ].length, | ||
link | ||
]; | ||
} | ||
// Just consume the '[' | ||
return [ 1, "[" ]; | ||
} | ||
/** | ||
@@ -375,7 +357,2 @@ * Markdown.dialects.Gruber | ||
block: { | ||
htmlBlock: function htmlBlock( block, next ) { | ||
if ( block.match( /^\s*<\w/ ) && block.match( /<\/\s*\w+\s*>\s*$/ ) ) | ||
return [["__RAW", block.toString()]]; | ||
}, | ||
atxHeader: function atxHeader( block, next ) { | ||
@@ -421,6 +398,4 @@ var m = block.match( /^(#{1,6})\s*(.*?)\s*#*\s*(?:\n|$)/ ); | ||
// 4 spaces + content | ||
var m = block.match( re ); | ||
if ( !block.match( re ) ) return undefined; | ||
if ( !m ) return undefined; | ||
block_search: | ||
@@ -430,3 +405,3 @@ do { | ||
var b = this.loop_re_over_block( | ||
re, block.valueOf(), function( m ) { ret.push( m[1] ) } ); | ||
re, block.valueOf(), function( m ) { ret.push( m[1] ); } ); | ||
@@ -440,6 +415,4 @@ if (b.length) { | ||
// Check the next block - it might be code too | ||
var m = next[0].match( re ); | ||
if ( !next[0].match( re ) ) break block_search; | ||
if ( !m ) break block_search; | ||
// Pull how how many blanks lines follow - minus two to account for .join | ||
@@ -450,4 +423,5 @@ ret.push ( block.trailing.replace(/[^\n]/g, '').substring(2) ); | ||
} | ||
else | ||
else { | ||
break block_search; | ||
} | ||
} while (true); | ||
@@ -500,3 +474,3 @@ | ||
// Use a closure to hide a few variables. | ||
var any_list = "[*+-]|\\d\\.", | ||
var any_list = "[*+-]|\\d+\\.", | ||
bullet_list = /[*+-]/, | ||
@@ -541,4 +515,3 @@ number_list = /\d+\./, | ||
is_str = typeof what == "string"; | ||
if (is_str && add_to.length > 1 && typeof add_to[add_to.length-1] == "string" ) | ||
{ | ||
if (is_str && add_to.length > 1 && typeof add_to[add_to.length-1] == "string" ) { | ||
add_to[ add_to.length-1 ] += what; | ||
@@ -611,3 +584,4 @@ } | ||
loose = false, | ||
ret = [ stack[0].list ]; | ||
ret = [ stack[0].list ], | ||
i; | ||
@@ -628,3 +602,3 @@ // Loop to search over block looking for inner block elements and loose lists | ||
var nl = "", | ||
l = lines[line_no].replace(/^\n/, function(n) { nl = n; return "" }); | ||
l = lines[line_no].replace(/^\n/, function(n) { nl = n; return ""; }); | ||
@@ -662,9 +636,10 @@ // TODO: really should cache this | ||
// wanted_depth deserves. | ||
var found = stack.some(function(s, i) { | ||
if ( s.indent != m[1] ) return false; | ||
list = s.list; // Found the level we want | ||
stack.splice(i+1); // Remove the others | ||
//print("found"); | ||
return true; // And stop looping | ||
}); | ||
var found = false; | ||
for (i = 0; i < stack.length; i++) { | ||
if ( stack[ i ].indent != m[1] ) continue; | ||
list = stack[ i ].list; | ||
stack.splice( i+1 ); | ||
found = true; | ||
break; | ||
} | ||
@@ -714,3 +689,3 @@ if (!found) { | ||
// Make sure all listitems up the stack are paragraphs | ||
stack.forEach( paragraphify, this ); | ||
forEach( stack, paragraphify, this); | ||
@@ -734,3 +709,3 @@ last_li.push.apply( last_li, this.toTree( contained, [] ) ); | ||
// Make sure all listitems up the stack are paragraphs | ||
stack.forEach( paragraphify , this ); | ||
forEach( stack, paragraphify, this); | ||
@@ -744,3 +719,3 @@ loose = true; | ||
return ret; | ||
} | ||
}; | ||
})(), | ||
@@ -772,3 +747,3 @@ | ||
var b = next.shift(); | ||
block += block.trailing + b; | ||
block = new String(block + block.trailing + b); | ||
block.trailing = b.trailing; | ||
@@ -831,28 +806,24 @@ } | ||
} | ||
} | ||
}; | ||
Markdown.dialects.Gruber.inline = { | ||
__call__: function inline( text, patterns ) { | ||
// Hmmm - should this function be directly in Md#processInline, or | ||
// conversely, should Md#processBlock be moved into block.__call__ too | ||
var out = [ ], | ||
m, | ||
// Look for the next occurange of a special character/pattern | ||
re = new RegExp( "([\\s\\S]*?)(" + (patterns.source || patterns) + ")", "g" ), | ||
__oneElement__: function oneElement( text, patterns_or_re, previous_nodes ) { | ||
var m, | ||
res, | ||
lastIndex = 0; | ||
//D:var self = this; | ||
//D:self.debug("processInline:", uneval(text) ); | ||
function add(x) { | ||
//D:self.debug(" adding output", uneval(x)); | ||
if (typeof x == "string" && typeof out[out.length-1] == "string") | ||
out[ out.length-1 ] += x; | ||
else | ||
out.push(x); | ||
patterns_or_re = patterns_or_re || Markdown.dialects.Gruber.inline.__patterns__; | ||
var re = new RegExp( "([\\s\\S]*?)(" + (patterns_or_re.source || patterns_or_re) + ")" ); | ||
m = re.exec( text ); | ||
if (!m) { | ||
// Just boring text | ||
return [ text.length, text ]; | ||
} | ||
else if ( m[1] ) { | ||
// Some un-interesting text matched. Return that first | ||
return [ m[1].length, m[1] ]; | ||
} | ||
while ( ( m = re.exec(text) ) != null) { | ||
if ( m[1] ) add( m[1] ); // Some un-interesting text matched | ||
else m[1] = { length: 0 }; // Or there was none, but make m[1].length == 0 | ||
var res; | ||
@@ -862,20 +833,27 @@ if ( m[2] in this.dialect.inline ) { | ||
this, | ||
text.substr( m.index + m[1].length ), m, out ); | ||
text.substr( m.index ), m, previous_nodes || [] ); | ||
} | ||
// Default for now to make dev easier. just slurp special and output it. | ||
res = res || [ m[2].length, m[2] ]; | ||
return res; | ||
}, | ||
var len = res.shift(); | ||
// Update how much input was consumed | ||
re.lastIndex += ( len - m[2].length ); | ||
__call__: function inline( text, patterns ) { | ||
// Add children | ||
res.forEach(add); | ||
var out = [], | ||
res; | ||
lastIndex = re.lastIndex; | ||
function add(x) { | ||
//D:self.debug(" adding output", uneval(x)); | ||
if (typeof x == "string" && typeof out[out.length-1] == "string") | ||
out[ out.length-1 ] += x; | ||
else | ||
out.push(x); | ||
} | ||
// Add last 'boring' chunk | ||
if ( text.length > lastIndex ) | ||
add( text.substr( lastIndex ) ); | ||
while ( text.length > 0 ) { | ||
res = Markdown.dialects.Gruber.inline.__oneElement__.call(this, text, patterns, out ); | ||
text = text.substr( res.shift() ); | ||
forEach(res, add ) | ||
} | ||
@@ -885,2 +863,7 @@ return out; | ||
// These characters are intersting elsewhere, so have rules for them so that | ||
// chunks of plain text blocks don't include them | ||
"]": function () {}, | ||
"}": function () {}, | ||
"\\": function escaped( text ) { | ||
@@ -897,2 +880,6 @@ // [ length of input processed, node/children to add... ] | ||
"![": function image( text ) { | ||
// Unlike images, alt text is plain text only. no other elements are | ||
// allowed in there | ||
// ![Alt text](/path/to/img.jpg "Optional title") | ||
@@ -906,3 +893,3 @@ // 1 2 3 4 <--- captures | ||
m[2] == this.dialect.inline.__call__.call( this, m[2], /\\/ )[0]; | ||
m[2] = this.dialect.inline.__call__.call( this, m[2], /\\/ )[0]; | ||
@@ -922,3 +909,3 @@ var attrs = { alt: m[1], href: m[2] || "" }; | ||
// found till after. Check it in md tree->hmtl tree conversion | ||
return [ m[0].length, [ "img_ref", { alt: m[1], ref: m[2].toLowerCase(), text: m[0] } ] ]; | ||
return [ m[0].length, [ "img_ref", { alt: m[1], ref: m[2].toLowerCase(), original: m[0] } ] ]; | ||
} | ||
@@ -930,4 +917,95 @@ | ||
"[": Markdown.prototype.link, | ||
"[": function link( text ) { | ||
var orig = String(text); | ||
// Inline content is possible inside `link text` | ||
var res = Markdown.DialectHelpers.inline_until_char.call( this, text.substr(1), ']' ); | ||
// No closing ']' found. Just consume the [ | ||
if ( !res ) return [ 1, '[' ]; | ||
var consumed = 1 + res[ 0 ], | ||
children = res[ 1 ], | ||
link, | ||
attrs; | ||
// At this point the first [...] has been parsed. See what follows to find | ||
// out which kind of link we are (reference or direct url) | ||
text = text.substr( consumed ); | ||
// [link text](/path/to/img.jpg "Optional title") | ||
// 1 2 3 <--- captures | ||
// This will capture up to the last paren in the block. We then pull | ||
// back based on if there a matching ones in the url | ||
// ([here](/url/(test)) | ||
// The parens have to be balanced | ||
var m = text.match( /^\s*\([ \t]*(\S+)(?:[ \t]+(["'])(.*?)\2)?[ \t]*\)/ ); | ||
if ( m ) { | ||
var url = m[1]; | ||
consumed += m[0].length; | ||
if ( url && url[0] == '<' && url[url.length-1] == '>' ) | ||
url = url.substring( 1, url.length - 1 ); | ||
// If there is a title we don't have to worry about parens in the url | ||
if ( !m[3] ) { | ||
var open_parens = 1; // One open that isn't in the capture | ||
for (var len = 0; len < url.length; len++) { | ||
switch ( url[len] ) { | ||
case '(': | ||
open_parens++; | ||
break; | ||
case ')': | ||
if ( --open_parens == 0) { | ||
consumed -= url.length - len; | ||
url = url.substring(0, len); | ||
} | ||
break; | ||
} | ||
} | ||
} | ||
// Process escapes only | ||
url = Markdown.dialects.Gruber.inline.__call__.call( this, url, /\\/ )[0]; | ||
attrs = { href: url || "" }; | ||
if ( m[3] !== undefined) | ||
attrs.title = m[3]; | ||
link = [ "link", attrs ].concat( children ); | ||
return [ consumed, link ]; | ||
} | ||
// [Alt text][id] | ||
// [Alt text] [id] | ||
m = text.match( /^\s*\[(.*?)\]/ ); | ||
if ( m ) { | ||
consumed += m[ 0 ].length; | ||
// [links][] uses links as its reference | ||
attrs = { ref: ( m[ 1 ] || String(children) ).toLowerCase(), original: orig.substr( 0, consumed ) }; | ||
link = [ "link_ref", attrs ].concat( children ); | ||
// We can't check if the reference is known here as it likely wont be | ||
// found till after. Check it in md tree->hmtl tree conversion. | ||
// Store the original so that conversion can revert if the ref isn't found. | ||
return [ consumed, link ]; | ||
} | ||
// [id] | ||
// Only if id is plain (no formatting.) | ||
if ( children.length == 1 && typeof children[0] == "string" ) { | ||
attrs = { ref: children[0].toLowerCase(), original: orig.substr( 0, consumed ) }; | ||
link = [ "link_ref", attrs, children[0] ]; | ||
return [ consumed, link ]; | ||
} | ||
// Just consume the '[' | ||
return [ 1, "[" ]; | ||
}, | ||
"<": function autoLink( text ) { | ||
@@ -962,10 +1040,71 @@ var m; | ||
} | ||
}, | ||
" \n": function lineBreak( text ) { | ||
return [ 3, [ "linebreak" ] ]; | ||
} | ||
}; | ||
// Meta Helper/generator method for em and strong handling | ||
function strong_em( tag, md ) { | ||
var state_slot = tag + "_state", | ||
other_slot = tag == "strong" ? "em_state" : "strong_state"; | ||
function CloseTag(len) { | ||
this.len_after = len; | ||
this.name = "close_" + md; | ||
} | ||
return function ( text, orig_match ) { | ||
if (this[state_slot][0] == md) { | ||
// Most recent em is of this type | ||
//D:this.debug("closing", md); | ||
this[state_slot].shift(); | ||
// "Consume" everything to go back to the recrusion in the else-block below | ||
return[ text.length, new CloseTag(text.length-md.length) ]; | ||
} | ||
else { | ||
// Store a clone of the em/strong states | ||
var other = this[other_slot].slice(), | ||
state = this[state_slot].slice(); | ||
this[state_slot].unshift(md); | ||
//D:this.debug_indent += " "; | ||
// Recurse | ||
var res = this.processInline( text.substr( md.length ) ); | ||
//D:this.debug_indent = this.debug_indent.substr(2); | ||
var last = res[res.length - 1]; | ||
//D:this.debug("processInline from", tag + ": ", uneval( res ) ); | ||
var check = this[state_slot].shift(); | ||
if (last instanceof CloseTag) { | ||
res.pop(); | ||
// We matched! Huzzah. | ||
var consumed = text.length - last.len_after; | ||
return [ consumed, [ tag ].concat(res) ]; | ||
} | ||
else { | ||
// Restore the state of the other kind. We might have mistakenly closed it. | ||
this[other_slot] = other; | ||
this[state_slot] = state; | ||
// We can't reuse the processed result as it could have wrong parsing contexts in it. | ||
return [ md.length, md ]; | ||
} | ||
} | ||
} // End returned function | ||
} | ||
Markdown.dialects.Gruber.inline["**"] = helpers.strong_em("strong", "**"); | ||
Markdown.dialects.Gruber.inline["__"] = helpers.strong_em("strong", "__"); | ||
Markdown.dialects.Gruber.inline["*"] = helpers.strong_em("em", "*"); | ||
Markdown.dialects.Gruber.inline["_"] = helpers.strong_em("em", "_"); | ||
Markdown.dialects.Gruber.inline["**"] = strong_em("strong", "**"); | ||
Markdown.dialects.Gruber.inline["__"] = strong_em("strong", "__"); | ||
Markdown.dialects.Gruber.inline["*"] = strong_em("em", "*"); | ||
Markdown.dialects.Gruber.inline["_"] = strong_em("em", "_"); | ||
@@ -981,3 +1120,3 @@ | ||
d.__order__ = ord; | ||
} | ||
}; | ||
@@ -989,3 +1128,4 @@ // Build patterns for inline matcher | ||
for ( var i in d ) { | ||
if (i == "__call__") continue; | ||
// __foo__ is reserved and not a pattern | ||
if ( i.match( /^__.*__$/) ) continue; | ||
var l = i.replace( /([\\.*+?|()\[\]{}])/g, "\\$1" ) | ||
@@ -997,2 +1137,3 @@ .replace( /\n/, "\\n" ); | ||
patterns = patterns.join("|"); | ||
d.__patterns__ = patterns; | ||
//print("patterns:", uneval( patterns ) ); | ||
@@ -1002,7 +1143,34 @@ | ||
d.__call__ = function(text, pattern) { | ||
if (pattern != undefined) | ||
if (pattern != undefined) { | ||
return fn.call(this, text, pattern); | ||
} | ||
else | ||
{ | ||
return fn.call(this, text, patterns); | ||
} | ||
}; | ||
}; | ||
Markdown.DialectHelpers = {}; | ||
Markdown.DialectHelpers.inline_until_char = function( text, want ) { | ||
var consumed = 0, | ||
nodes = []; | ||
while ( true ) { | ||
if ( text[ consumed ] == want ) { | ||
// Found the character we were looking for | ||
consumed++; | ||
return [ consumed, nodes ]; | ||
} | ||
if ( consumed >= text.length ) { | ||
// No closing char found. Abort. | ||
return null; | ||
} | ||
res = Markdown.dialects.Gruber.inline.__oneElement__.call(this, text.substr( consumed ) ); | ||
consumed += res[ 0 ]; | ||
// Add any returned nodes. | ||
nodes.push.apply( nodes, res.slice( 1 ) ); | ||
} | ||
} | ||
@@ -1012,9 +1180,9 @@ | ||
Markdown.subclassDialect = function( d ) { | ||
function Block() {}; | ||
function Block() {} | ||
Block.prototype = d.block; | ||
function Inline() {}; | ||
function Inline() {} | ||
Inline.prototype = d.inline; | ||
return { block: new Block(), inline: new Inline() }; | ||
} | ||
}; | ||
@@ -1026,2 +1194,67 @@ Markdown.buildBlockOrder ( Markdown.dialects.Gruber.block ); | ||
Markdown.dialects.Maruku.processMetaHash = function processMetaHash( meta_string ) { | ||
var meta = split_meta_hash( meta_string ), | ||
attr = {}; | ||
for ( var i = 0; i < meta.length; ++i ) { | ||
// id: #foo | ||
if ( /^#/.test( meta[ i ] ) ) { | ||
attr.id = meta[ i ].substring( 1 ); | ||
} | ||
// class: .foo | ||
else if ( /^\./.test( meta[ i ] ) ) { | ||
// if class already exists, append the new one | ||
if ( attr['class'] ) { | ||
attr['class'] = attr['class'] + meta[ i ].replace( /./, " " ); | ||
} | ||
else { | ||
attr['class'] = meta[ i ].substring( 1 ); | ||
} | ||
} | ||
// attribute: foo=bar | ||
else if ( /\=/.test( meta[ i ] ) ) { | ||
var s = meta[ i ].split( /\=/ ); | ||
attr[ s[ 0 ] ] = s[ 1 ]; | ||
} | ||
} | ||
return attr; | ||
} | ||
function split_meta_hash( meta_string ) { | ||
var meta = meta_string.split( "" ), | ||
parts = [ "" ], | ||
in_quotes = false; | ||
while ( meta.length ) { | ||
var letter = meta.shift(); | ||
switch ( letter ) { | ||
case " " : | ||
// if we're in a quoted section, keep it | ||
if ( in_quotes ) { | ||
parts[ parts.length - 1 ] += letter; | ||
} | ||
// otherwise make a new part | ||
else { | ||
parts.push( "" ); | ||
} | ||
break; | ||
case "'" : | ||
case '"' : | ||
// reverse the quotes and move straight on | ||
in_quotes = !in_quotes; | ||
break; | ||
case "\\" : | ||
// shift off the next letter to be used straight away. | ||
// it was escaped so we'll keep it whatever it is | ||
letter = meta.shift(); | ||
default : | ||
parts[ parts.length - 1 ] += letter; | ||
break; | ||
} | ||
} | ||
return parts; | ||
} | ||
Markdown.dialects.Maruku.block.document_meta = function document_meta( block, next ) { | ||
@@ -1051,3 +1284,3 @@ // we're only interested in the first block | ||
return []; | ||
} | ||
}; | ||
@@ -1060,7 +1293,9 @@ Markdown.dialects.Maruku.block.block_meta = function block_meta( block, next ) { | ||
// process the meta hash | ||
var attr = process_meta_hash( m[ 2 ] ); | ||
var attr = Markdown.dialects.Maruku.processMetaHash( m[ 2 ] ); | ||
var hash; | ||
// if we matched ^ then we need to apply meta to the previous block | ||
if ( m[ 1 ] === "" ) { | ||
var node = this.tree[ this.tree.length - 1 ], | ||
var node = this.tree[ this.tree.length - 1 ]; | ||
hash = extract_attr( node ); | ||
@@ -1091,3 +1326,3 @@ | ||
// get or make the attributes hash | ||
var hash = extract_attr( result[ 0 ] ); | ||
hash = extract_attr( result[ 0 ] ); | ||
if ( !hash ) { | ||
@@ -1104,8 +1339,9 @@ hash = {}; | ||
return result; | ||
} | ||
}; | ||
Markdown.dialects.Maruku.block.definition_list = function definition_list( block, next ) { | ||
// one or more terms followed by one or more definitions, in a single block | ||
var tight = /^((?:[^\s:].*\n)+):\s+([^]+)$/, | ||
list = [ "dl" ]; | ||
var tight = /^((?:[^\s:].*\n)+):\s+([\s\S]+)$/, | ||
list = [ "dl" ], | ||
i; | ||
@@ -1128,7 +1364,7 @@ // see if we're dealing with a tight or loose block | ||
for ( var i = 0; i < terms.length; ++i ) { | ||
for ( i = 0; i < terms.length; ++i ) { | ||
list.push( [ "dt", terms[ i ] ] ); | ||
} | ||
for ( var i = 0; i < defns.length; ++i ) { | ||
for ( i = 0; i < defns.length; ++i ) { | ||
// run inline processing over the definition | ||
@@ -1144,3 +1380,3 @@ list.push( [ "dd" ].concat( this.processInline( defns[ i ].replace( /(\n)\s+/, "$1" ) ) ) ); | ||
return [ list ]; | ||
} | ||
}; | ||
@@ -1168,3 +1404,4 @@ Markdown.dialects.Maruku.inline[ "{:" ] = function inline_meta( text, matches, out ) { | ||
// attach the attributes to the preceeding element | ||
var meta = process_meta_hash( m[ 1 ] ), | ||
var meta = Markdown.dialects.Maruku.processMetaHash( m[ 1 ] ), | ||
attr = extract_attr( before ); | ||
@@ -1183,3 +1420,3 @@ | ||
return [ m[ 0 ].length, "" ]; | ||
} | ||
}; | ||
@@ -1189,3 +1426,2 @@ Markdown.buildBlockOrder ( Markdown.dialects.Maruku.block ); | ||
Markdown.dialects.GFM = Markdown.subclassDialect( Markdown.dialects.Maruku ); | ||
@@ -1268,25 +1504,21 @@ | ||
if ( !m ) { | ||
return Markdown.prototype.link( text ); | ||
return Markdown.dialects.Gruber.inline["["]( text ); | ||
} | ||
if ( m ) | ||
{ | ||
// attach the attributes to the preceeding element | ||
var meta = process_meta_hash( m[ 2 ] ), | ||
attr = extract_attr( m[ 1 ] ); | ||
// attach the attributes to the preceeding element | ||
var meta = Markdown.dialects.Maruku.processMetaHash( m[ 2 ] ), | ||
attr = extract_attr( m[ 1 ] ); | ||
if ( !attr ) { | ||
attr = {}; | ||
} | ||
if ( !attr ) { | ||
attr = {}; | ||
} | ||
for ( var k in meta ) { | ||
attr[k] = meta[k]; | ||
} | ||
// cut out the string and replace it with span + attribute | ||
var span = [ "span", attr ]; | ||
Array.prototype.push.apply( span, this.processInline( m[1] ) ); | ||
return [ m[0].length, span ]; | ||
for ( var k in meta ) { | ||
attr[k] = meta[k]; | ||
} | ||
// cut out the string and replace it with span + attribute | ||
var span = [ "span", attr ]; | ||
Array.prototype.push.apply( span, this.processInline( m[1] ) ); | ||
return [ m[0].length, span ]; | ||
} | ||
@@ -1352,4 +1584,4 @@ | ||
var tHeaders = lines[0]; | ||
var tableRERow = /^|.+/; // lazy. | ||
var tableRERow = /^\|.+/; // lazy. | ||
if (tHeaders.length) { | ||
@@ -1382,3 +1614,3 @@ var tr = [ "tr" ] | ||
for (var j = 1; j < rows.length; j++) { | ||
tr.push( ["td", {align: alignAttr[j]}].concat(rows[j]) ); | ||
tr.push( ["td", {align: alignAttr[j]}].concat( this.processInline(rows[j]) )); | ||
} | ||
@@ -1394,6 +1626,20 @@ } | ||
return [ table ]; | ||
if (lines[lines.length - 1].match(/^{: /)) { | ||
var meta = Markdown.dialects.Maruku.processMetaHash( lines[lines.length - 1] ), | ||
attr = extract_attr( table ); | ||
if ( !attr ) { | ||
attr = {}; | ||
table.splice( 1, 0, attr ); | ||
} | ||
for ( var k in meta ) { | ||
attr[ k ] = meta[ k ]; | ||
} | ||
} | ||
return [ ["table", table ] ]; | ||
} | ||
Markdown.dialects.NAMP.block.table2 = function table1_maker( block, next ) { | ||
Markdown.dialects.NAMP.block.table2 = function table2_maker( block, next ) { | ||
var tableRE = /^[ ]{0,3}(\S.*[|].*)\n[ ]{0,3}([-:]+[ ]*[|][-| :]*)\n/; | ||
@@ -1409,4 +1655,2 @@ | ||
var tableRERow = /^[^{:]/; // lazy. | ||
var metadataRE = /^{:/; | ||
var metadata = ""; | ||
@@ -1440,9 +1684,6 @@ if (tHeaders.length) { | ||
for (var j = 0; j < rows.length; j++) { | ||
tr.push( ["td", {align: alignAttr[j]}].concat(rows[j]) ); | ||
tr.push( ["td", {align: alignAttr[j]}].concat( this.processInline(rows[j]) )); | ||
} | ||
table.push(tr); | ||
} | ||
else if ( (r = lines[i].match(metadataRE ) ) ) { | ||
metadata = lines[i]; | ||
} | ||
} | ||
@@ -1454,2 +1695,16 @@ } | ||
if (lines[lines.length - 1].match(/^{: /)) { | ||
var meta = Markdown.dialects.Maruku.processMetaHash( lines[lines.length - 1] ), | ||
attr = extract_attr( table ); | ||
if ( !attr ) { | ||
attr = {}; | ||
table.splice( 1, 0, attr ); | ||
} | ||
for ( var k in meta ) { | ||
attr[ k ] = meta[ k ]; | ||
} | ||
} | ||
return [ table ]; | ||
@@ -1461,8 +1716,26 @@ } | ||
var isArray = Array.isArray || function(obj) { | ||
return Object.prototype.toString.call(obj) == '[object Array]'; | ||
}; | ||
var forEach; | ||
// Don't mess with Array.prototype. Its not friendly | ||
if ( Array.prototype.forEach ) { | ||
forEach = function( arr, cb, thisp ) { | ||
return arr.forEach( cb, thisp ); | ||
}; | ||
} | ||
else { | ||
forEach = function(arr, cb, thisp) { | ||
for (var i = 0; i < arr.length; i++) { | ||
cb.call(thisp || arr, arr[i], i, arr); | ||
} | ||
} | ||
} | ||
function extract_attr( jsonml ) { | ||
return helpers.isArray(jsonml) | ||
return isArray(jsonml) | ||
&& jsonml.length > 1 | ||
&& typeof jsonml[ 1 ] === "object" | ||
&& !( helpers.isArray(jsonml[ 1 ]) ) | ||
&& !( isArray(jsonml[ 1 ]) ) | ||
? jsonml[ 1 ] | ||
@@ -1472,67 +1745,4 @@ : undefined; | ||
function process_meta_hash( meta_string ) { | ||
var meta = split_meta_hash( meta_string ), | ||
attr = {}; | ||
for ( var i = 0; i < meta.length; ++i ) { | ||
// id: #foo | ||
if ( /^#/.test( meta[ i ] ) ) { | ||
attr.id = meta[ i ].substring( 1 ); | ||
} | ||
// class: .foo | ||
else if ( /^\./.test( meta[ i ] ) ) { | ||
// if class already exists, append the new one | ||
if ( attr['class'] ) { | ||
attr['class'] = attr['class'] + meta[ i ].replace( /./, " " ); | ||
} | ||
else { | ||
attr['class'] = meta[ i ].substring( 1 ); | ||
} | ||
} | ||
// attribute: foo=bar | ||
else if ( /=/.test( meta[ i ] ) ) { | ||
var s = meta[ i ].split( /=/ ); | ||
attr[ s[ 0 ] ] = s[ 1 ]; | ||
} | ||
} | ||
return attr; | ||
} | ||
function split_meta_hash( meta_string ) { | ||
var meta = meta_string.split( "" ), | ||
parts = [ "" ], | ||
in_quotes = false; | ||
while ( meta.length ) { | ||
var letter = meta.shift(); | ||
switch ( letter ) { | ||
case " " : | ||
// if we're in a quoted section, keep it | ||
if ( in_quotes ) { | ||
parts[ parts.length - 1 ] += letter; | ||
} | ||
// otherwise make a new part | ||
else { | ||
parts.push( "" ); | ||
} | ||
break; | ||
case "'" : | ||
case '"' : | ||
// reverse the quotes and move straight on | ||
in_quotes = !in_quotes; | ||
break; | ||
case "\\" : | ||
// shift off the next letter to be used straight away. | ||
// it was escaped so we'll keep it whatever it is | ||
letter = meta.shift(); | ||
default : | ||
parts[ parts.length - 1 ] += letter; | ||
break; | ||
} | ||
} | ||
return parts; | ||
} | ||
/** | ||
@@ -1573,10 +1783,10 @@ * renderJsonML( jsonml[, options] ) -> String | ||
return content.join( "\n\n" ); | ||
} | ||
}; | ||
function escapeHTML( text ) { | ||
return text.replace( /&/g, "&" ) | ||
.replace( /</g, "<" ) | ||
.replace( />/g, ">" ) | ||
.replace( /"/g, """ ) | ||
.replace( /'/g, "'" ); | ||
.replace( /</g, "<" ) | ||
.replace( />/g, ">" ) | ||
.replace( /"/g, """ ) | ||
.replace( /'/g, "'" ); | ||
} | ||
@@ -1604,5 +1814,2 @@ | ||
} | ||
if ( jsonml[0] == "__RAW" ) { | ||
return jsonml[1]; | ||
} | ||
@@ -1626,10 +1833,13 @@ var tag = jsonml.shift(), | ||
if (tag == 'br') | ||
return "<br />"; | ||
// be careful about adding whitespace here for inline elements | ||
if ( tag == "img" || tag == "br" || tag == "hr" ) { | ||
return "<"+ tag + tag_attrs + "/>"; | ||
} | ||
else { | ||
return "<"+ tag + tag_attrs + ">" + content.join( "" ) + "</" + tag + ">"; | ||
} | ||
} | ||
function convert_tree_to_html( tree, references, options ) { | ||
var i; | ||
options = options || {}; | ||
@@ -1648,3 +1858,3 @@ | ||
jsonml[ 1 ] = {}; | ||
for ( var i in attrs ) { | ||
for ( i in attrs ) { | ||
jsonml[ 1 ][ i ] = attrs[ i ]; | ||
@@ -1659,3 +1869,3 @@ } | ||
} | ||
//console.log(jsonml[ 0 ] ); | ||
// convert this node | ||
@@ -1685,12 +1895,5 @@ switch ( jsonml[ 0 ] ) { | ||
jsonml[ 0 ] = "pre"; | ||
var i = attrs ? 2 : 1; | ||
i = attrs ? 2 : 1; | ||
var code = [ "code" ]; | ||
code.push.apply( code, jsonml.splice( i ) ); | ||
if (attrs !== undefined && attrs.lang !== undefined) | ||
code.push( [ attrs.lang ] ); | ||
jsonml[ i ] = code; | ||
@@ -1713,2 +1916,3 @@ break; | ||
jsonml[ 0 ] = "div"; | ||
break; | ||
case "link": | ||
@@ -1741,6 +1945,30 @@ jsonml[ 0 ] = "a"; | ||
break; | ||
case "img_ref": | ||
jsonml[ 0 ] = "img"; | ||
// grab this ref and clean up the attribute node | ||
var ref = references[ attrs.ref ]; | ||
// if the reference exists, make the link | ||
if ( ref ) { | ||
delete attrs.ref; | ||
// add in the href and title, if present | ||
attrs.src = ref.href; | ||
if ( ref.title ) { | ||
attrs.title = ref.title; | ||
} | ||
// get rid of the unneeded original text | ||
delete attrs.original; | ||
} | ||
// the reference doesn't exist, so revert to plain text | ||
else { | ||
return attrs.original; | ||
} | ||
break; | ||
} | ||
// convert all the children | ||
var i = 1; | ||
i = 1; | ||
@@ -1747,0 +1975,0 @@ // deal with the attribute node, if it exists |
{ | ||
"name" : "namp", | ||
"version" : "0.1.4", | ||
"version" : "0.1.6", | ||
"description" : "Markdown parser for Node, with Maruku, GFM, and PHP Extras support, plus more.", | ||
@@ -5,0 +5,0 @@ "keywords" : [ "markdown", "maruku", "gfm", "text processing", "ast" ], |
@@ -1,28 +0,28 @@ | ||
var markdown = require('Markdown'); | ||
var markdown = require("../lib/markdown"), | ||
test = require("tap").test; | ||
// var markdown = require('Markdown'); | ||
function clone_array( input ) { | ||
eval( "var tmp = " + input.toSource() ); | ||
return tmp; | ||
// Helper method. Since the objects are plain round trip through JSON to get | ||
// a clone | ||
return JSON.parse( JSON.stringify( input ) ); | ||
} | ||
tests = { | ||
test_arguments_untouched: function() { | ||
var input = "A [link][id] by id.\n\n[id]: http://google.com", | ||
tree = markdown.parse( input ), | ||
clone = clone_array( tree ); | ||
test("arguments untouched", function(t) { | ||
var input = "A [link][id] by id.\n\n[id]: http://google.com", | ||
tree = markdown.parse( input ), | ||
clone = clone_array( tree ); | ||
var output = markdown.toHTML( tree ); | ||
var output = markdown.toHTML( tree ); | ||
asserts.same( tree, clone, "tree isn't modified" ); | ||
// We had a problem where we would acidentally remove the references | ||
// property from the root. We want to check the output is the same when | ||
// called twice. | ||
asserts.same( markdown.toHTML( tree ), output, "output is consistent" ); | ||
} | ||
} | ||
t.equivalent( tree, clone, "tree isn't modified" ); | ||
// We had a problem where we would accidentally remove the references | ||
// property from the root. We want to check the output is the same when | ||
// called twice. | ||
t.equivalent( markdown.toHTML( tree ), output, "output is consistent" ); | ||
if (require.main === module) { | ||
var asserts = require('test').asserts; | ||
require('test').runner(tests); | ||
} | ||
t.end(); | ||
}); | ||
@@ -1,263 +0,266 @@ | ||
var Markdown = require('Markdown').Markdown, | ||
var markdown = require("../lib/markdown"), | ||
tap = require("tap"), | ||
Markdown = markdown.Markdown, | ||
mk_block = Markdown.mk_block; | ||
var tests = { | ||
meta: function(fn) { | ||
return function() { fn( new Markdown ) } | ||
} | ||
}; | ||
tests = { | ||
test_split_block: tests.meta(function(md) { | ||
asserts.same( | ||
md.split_blocks( "# h1 #\n\npara1\npara1L2\n \n\n\n\npara2\n" ), | ||
[mk_block( "# h1 #", "\n\n", 1 ), | ||
mk_block( "para1\npara1L2", "\n \n\n\n\n", 3 ), | ||
mk_block( "para2", "\n", 9 ) | ||
], | ||
"split_block should record trailing newlines"); | ||
/* | ||
* This file contains tests that check various regressions on the individual | ||
* parsers, rather than the parser as a whole. | ||
*/ | ||
asserts.same( | ||
md.split_blocks( "\n\n# heading #\n\npara\n" ), | ||
[mk_block( "# heading #", "\n\n", 3 ), | ||
mk_block( "para", "\n", 5 ) | ||
], | ||
"split_block should ignore leading newlines"); | ||
}), | ||
function test(name, cb) { | ||
tap.test( name, function(t) { | ||
cb(t, new Markdown ); | ||
t.end(); | ||
}); | ||
}; | ||
test_headers: tests.meta(function(md) { | ||
var h1 = md.dialect.block.atxHeader( "# h1 #\n\n", [] ), | ||
h2; | ||
test("split_block", function(t, md) { | ||
t.equivalent( | ||
md.split_blocks( "# h1 #\n\npara1\npara1L2\n \n\n\n\npara2\n" ), | ||
[mk_block( "# h1 #", "\n\n", 1 ), | ||
mk_block( "para1\npara1L2", "\n \n\n\n\n", 3 ), | ||
mk_block( "para2", "\n", 9 ) | ||
], | ||
"split_block should record trailing newlines"); | ||
asserts.same( | ||
h1, | ||
md.dialect.block.setextHeader( "h1\n===\n\n", [] ), | ||
"Atx and Setext style H1s should produce the same output" ); | ||
t.equivalent( | ||
md.split_blocks( "\n\n# heading #\n\npara\n" ), | ||
[mk_block( "# heading #", "\n\n", 3 ), | ||
mk_block( "para", "\n", 5 ) | ||
], | ||
"split_block should ignore leading newlines"); | ||
}); | ||
asserts.same( | ||
md.dialect.block.atxHeader("# h1\n\n"), | ||
h1, | ||
"Closing # optional on atxHeader"); | ||
test("headers", function(t, md) { | ||
t.equivalent( | ||
md.dialect.block.setextHeader( "h1\n===\n\n", [] ), | ||
[ [ 'header', { level: 1 }, 'h1' ] ], | ||
"Atx and Setext style H1s should produce the same output" ); | ||
asserts.same( | ||
h2 = md.dialect.block.atxHeader( "## h2\n\n", [] ), | ||
[["header", {level: 2}, "h2"]], | ||
"Atx h2 has right level"); | ||
t.equivalent( | ||
md.dialect.block.atxHeader.call( md, "# h1\n\n"), | ||
[ [ 'header', { level: 1 }, 'h1' ] ], | ||
"Closing # optional on atxHeader"); | ||
asserts.same( | ||
h2, | ||
md.dialect.block.setextHeader( "h2\n---\n\n", [] ), | ||
"Atx and Setext style H2s should produce the same output" ); | ||
t.equivalent( | ||
h2 = md.dialect.block.atxHeader.call( md, "## h2\n\n", [] ), | ||
[["header", {level: 2}, "h2"]], | ||
"Atx h2 has right level"); | ||
}), | ||
t.equivalent( | ||
md.dialect.block.setextHeader.call( md, "h2\n---\n\n", [] ), | ||
[["header", {level: 2}, "h2"]], | ||
"Atx and Setext style H2s should produce the same output" ); | ||
}); | ||
test_code: tests.meta(function(md) { | ||
var code = md.dialect.block.code, | ||
next = [ mk_block("next") ]; | ||
test("code", function(t, md) { | ||
var code = md.dialect.block.code, | ||
next = [ mk_block("next") ]; | ||
asserts.same( | ||
code.call( md, mk_block(" foo\n bar"), next ), | ||
[["code_block", "foo\nbar" ]], | ||
"Code block correct"); | ||
t.equivalent( | ||
code.call( md, mk_block(" foo\n bar"), next ), | ||
[["code_block", "foo\nbar" ]], | ||
"Code block correct"); | ||
asserts.same( | ||
next, [mk_block("next")], | ||
"next untouched when its not code"); | ||
t.equivalent( | ||
next, [mk_block("next")], | ||
"next untouched when its not code"); | ||
next = []; | ||
asserts.same( | ||
code.call( md, mk_block(" foo\n bar"), next ), | ||
[["code_block", "foo" ]], | ||
"Code block correct for abutting para"); | ||
next = []; | ||
t.equivalent( | ||
code.call( md, mk_block(" foo\n bar"), next ), | ||
[["code_block", "foo" ]], | ||
"Code block correct for abutting para"); | ||
asserts.same( | ||
next, [mk_block(" bar")], | ||
"paragraph put back into next block"); | ||
t.equivalent( | ||
next, [mk_block(" bar")], | ||
"paragraph put back into next block"); | ||
asserts.same( | ||
code.call( md, mk_block(" foo"), [mk_block(" bar"), ] ), | ||
[["code_block", "foo\n\nbar" ]], | ||
"adjacent code blocks "); | ||
t.equivalent( | ||
code.call( md, mk_block(" foo"), [mk_block(" bar"), ] ), | ||
[["code_block", "foo\n\nbar" ]], | ||
"adjacent code blocks "); | ||
asserts.same( | ||
code.call( md, mk_block(" foo","\n \n \n"), [mk_block(" bar"), ] ), | ||
[["code_block", "foo\n\n\nbar" ]], | ||
"adjacent code blocks preserve correct number of empty lines"); | ||
t.equivalent( | ||
code.call( md, mk_block(" foo","\n \n \n"), [mk_block(" bar"), ] ), | ||
[["code_block", "foo\n\n\nbar" ]], | ||
"adjacent code blocks preserve correct number of empty lines"); | ||
}), | ||
}); | ||
test_bulletlist: tests.meta(function(md) { | ||
var bl = function() { return md.dialect.block.lists.apply(md, arguments) }; | ||
test( "bulletlist", function(t, md) { | ||
var bl = function() { return md.dialect.block.lists.apply(md, arguments) }; | ||
asserts.same( | ||
bl( mk_block("* foo\n* bar"), [] ), | ||
[ [ "bulletlist", [ "listitem", "foo" ], [ "listitem", "bar" ] ] ], | ||
"single line bullets"); | ||
t.equivalent( | ||
bl( mk_block("* foo\n* bar"), [] ), | ||
[ [ "bulletlist", [ "listitem", "foo" ], [ "listitem", "bar" ] ] ], | ||
"single line bullets"); | ||
asserts.same( | ||
bl( mk_block("* [text](url)" ), [] ), | ||
[ [ "bulletlist", [ "listitem", [ "link", { href: "url" }, "text" ] ] ] ], | ||
"link in bullet"); | ||
t.equivalent( | ||
bl( mk_block("* [text](url)" ), [] ), | ||
[ [ "bulletlist", [ "listitem", [ "link", { href: "url" }, "text" ] ] ] ], | ||
"link in bullet"); | ||
asserts.same( | ||
bl( mk_block("* foo\nbaz\n* bar\nbaz"), [] ), | ||
[ [ "bulletlist", [ "listitem", "foo\nbaz" ], [ "listitem", "bar\nbaz" ] ] ], | ||
"multiline lazy bullets"); | ||
t.equivalent( | ||
bl( mk_block("* foo\nbaz\n* bar\nbaz"), [] ), | ||
[ [ "bulletlist", [ "listitem", "foo\nbaz" ], [ "listitem", "bar\nbaz" ] ] ], | ||
"multiline lazy bullets"); | ||
asserts.same( | ||
bl( mk_block("* foo\n baz\n* bar\n baz"), [] ), | ||
[ [ "bulletlist", [ "listitem", "foo\nbaz" ], [ "listitem", "bar\nbaz" ] ] ], | ||
"multiline tidy bullets"); | ||
t.equivalent( | ||
bl( mk_block("* foo\n baz\n* bar\n baz"), [] ), | ||
[ [ "bulletlist", [ "listitem", "foo\nbaz" ], [ "listitem", "bar\nbaz" ] ] ], | ||
"multiline tidy bullets"); | ||
asserts.same( | ||
bl( mk_block("* foo\n baz"), [] ), | ||
[ [ "bulletlist", [ "listitem", "foo\n baz" ] ] ], | ||
"only trim 4 spaces from the start of the line"); | ||
t.equivalent( | ||
bl( mk_block("* foo\n baz"), [] ), | ||
[ [ "bulletlist", [ "listitem", "foo\n baz" ] ] ], | ||
"only trim 4 spaces from the start of the line"); | ||
/* Test wrong: should end up with 3 nested lists here | ||
asserts.same( | ||
bl( mk_block(" * one\n * two\n * three" ), [] ), | ||
[ [ "bulletlist", [ "listitem", "one" ], [ "listitem", "two" ], [ "listitem", "three" ] ] ], | ||
"bullets can be indented up to three spaces"); | ||
*/ | ||
/* Test wrong: should end up with 3 nested lists here | ||
t.equivalent( | ||
bl( mk_block(" * one\n * two\n * three" ), [] ), | ||
[ [ "bulletlist", [ "listitem", "one" ], [ "listitem", "two" ], [ "listitem", "three" ] ] ], | ||
"bullets can be indented up to three spaces"); | ||
*/ | ||
asserts.same( | ||
bl( mk_block(" * one"), [ mk_block(" two") ] ), | ||
[ [ "bulletlist", [ "listitem", [ "para", "one" ], [ "para", "two" ] ] ] ], | ||
"loose bullet lists can have multiple paragraphs"); | ||
t.equivalent( | ||
bl( mk_block(" * one"), [ mk_block(" two") ] ), | ||
[ [ "bulletlist", [ "listitem", [ "para", "one" ], [ "para", "two" ] ] ] ], | ||
"loose bullet lists can have multiple paragraphs"); | ||
/* Case: no space after bullet - not a list | ||
| *↵ | ||
|foo | ||
*/ | ||
asserts.same( | ||
bl( mk_block(" *\nfoo") ), | ||
undefined, | ||
"Space required after bullet to trigger list"); | ||
/* Case: no space after bullet - not a list | ||
| *↵ | ||
|foo | ||
*/ | ||
t.equivalent( | ||
bl( mk_block(" *\nfoo") ), | ||
undefined, | ||
"Space required after bullet to trigger list"); | ||
/* Case: note the space after the bullet | ||
| *␣ | ||
|foo | ||
|bar | ||
*/ | ||
asserts.same( | ||
bl( mk_block(" * \nfoo\nbar"), [ ] ), | ||
[ [ "bulletlist", [ "listitem", "foo\nbar" ] ] ], | ||
"space+continuation lines"); | ||
/* Case: note the space after the bullet | ||
| *␣ | ||
|foo | ||
|bar | ||
*/ | ||
t.equivalent( | ||
bl( mk_block(" * \nfoo\nbar"), [ ] ), | ||
[ [ "bulletlist", [ "listitem", "foo\nbar" ] ] ], | ||
"space+continuation lines", {todo: true} ); | ||
/* Case I: | ||
| * foo | ||
| * bar | ||
| * baz | ||
*/ | ||
asserts.same( | ||
bl( mk_block(" * foo\n" + | ||
" * bar\n" + | ||
" * baz"), | ||
[] ), | ||
[ [ "bulletlist", | ||
[ "listitem", | ||
"foo", | ||
[ "bulletlist", | ||
[ "listitem", | ||
"bar", | ||
[ "bulletlist", | ||
[ "listitem", "baz" ] | ||
] | ||
/* Case I: | ||
| * foo | ||
| * bar | ||
| * baz | ||
*/ | ||
t.equivalent( | ||
bl( mk_block(" * foo\n" + | ||
" * bar\n" + | ||
" * baz"), | ||
[] ), | ||
[ [ "bulletlist", | ||
[ "listitem", | ||
"foo", | ||
[ "bulletlist", | ||
[ "listitem", | ||
"bar", | ||
[ "bulletlist", | ||
[ "listitem", "baz" ] | ||
] | ||
] | ||
] | ||
] ], | ||
"Interesting indented lists I"); | ||
] | ||
] ], | ||
"Interesting indented lists I"); | ||
/* Case II: | ||
| * foo | ||
| * bar | ||
| * baz | ||
*/ | ||
asserts.same( | ||
bl( mk_block(" * foo\n * bar\n * baz"), [] ), | ||
[ [ "bulletlist", | ||
[ "listitem", | ||
"foo", | ||
[ "bulletlist", | ||
[ "listitem", "bar" ] | ||
] | ||
], | ||
[ "listitem", "baz" ] | ||
] ], | ||
"Interesting indented lists II"); | ||
/* Case II: | ||
| * foo | ||
| * bar | ||
| * baz | ||
*/ | ||
t.equivalent( | ||
bl( mk_block(" * foo\n * bar\n * baz"), [] ), | ||
[ [ "bulletlist", | ||
[ "listitem", | ||
"foo", | ||
[ "bulletlist", | ||
[ "listitem", "bar" ] | ||
] | ||
], | ||
[ "listitem", "baz" ] | ||
] ], | ||
"Interesting indented lists II"); | ||
/* Case III: | ||
| * foo | ||
| * bar | ||
|* baz | ||
| * fnord | ||
*/ | ||
asserts.same( | ||
bl( mk_block(" * foo\n * bar\n* baz\n * fnord"), [] ), | ||
[ [ "bulletlist", | ||
[ "listitem", | ||
"foo", | ||
[ "bulletlist", | ||
[ "listitem", "bar" ], | ||
[ "listitem", "baz" ], | ||
[ "listitem", "fnord" ] | ||
] | ||
/* Case III: | ||
| * foo | ||
| * bar | ||
|* baz | ||
| * fnord | ||
*/ | ||
t.equivalent( | ||
bl( mk_block(" * foo\n * bar\n* baz\n * fnord"), [] ), | ||
[ [ "bulletlist", | ||
[ "listitem", | ||
"foo", | ||
[ "bulletlist", | ||
[ "listitem", "bar" ], | ||
[ "listitem", "baz" ], | ||
[ "listitem", "fnord" ] | ||
] | ||
] ], | ||
"Interesting indented lists III"); | ||
] | ||
] ], | ||
"Interesting indented lists III"); | ||
/* Case IV: | ||
| * foo | ||
| | ||
| 1. bar | ||
*/ | ||
asserts.same( | ||
bl( mk_block(" * foo"), [ mk_block(" 1. bar\n") ] ), | ||
[ [ "bulletlist", | ||
["listitem", ["para", "foo"] ], | ||
["listitem", ["para", "bar"] ] | ||
] ], | ||
"Different lists at same indent IV"); | ||
/* Case IV: | ||
| * foo | ||
| | ||
| 1. bar | ||
*/ | ||
t.equivalent( | ||
bl( mk_block(" * foo"), [ mk_block(" 1. bar\n") ] ), | ||
[ [ "bulletlist", | ||
["listitem", ["para", "foo"] ], | ||
["listitem", ["para", "bar"] ] | ||
] ], | ||
"Different lists at same indent IV"); | ||
/* Case V: | ||
| * foo | ||
| * bar | ||
| * baz | ||
*/ | ||
asserts.same( | ||
bl( mk_block(" * foo\n * bar\n * baz"), [] ), | ||
[ [ "bulletlist", | ||
[ "listitem", | ||
"foo", | ||
[ "bulletlist", | ||
["listitem", "bar"], | ||
["listitem", "baz"], | ||
] | ||
/* Case V: | ||
| * foo | ||
| * bar | ||
| * baz | ||
*/ | ||
t.equivalent( | ||
bl( mk_block(" * foo\n * bar\n * baz"), [] ), | ||
[ [ "bulletlist", | ||
[ "listitem", | ||
"foo", | ||
[ "bulletlist", | ||
["listitem", "bar"], | ||
["listitem", "baz"], | ||
] | ||
] ], | ||
"Indenting Case V") | ||
] | ||
] ], | ||
"Indenting Case V") | ||
/* Case VI: deep nesting | ||
|* one | ||
| * two | ||
| * three | ||
| * four | ||
*/ | ||
asserts.same( | ||
bl( mk_block("* one\n * two\n * three\n * four"), [] ), | ||
[ [ "bulletlist", | ||
[ "listitem", | ||
"one", | ||
[ "bulletlist", | ||
[ "listitem", | ||
"two", | ||
[ "bulletlist", | ||
[ "listitem", | ||
"three", | ||
[ "bulletlist", | ||
[ "listitem", "four" ] | ||
] | ||
/* Case VI: deep nesting | ||
|* one | ||
| * two | ||
| * three | ||
| * four | ||
*/ | ||
t.equivalent( | ||
bl( mk_block("* one\n * two\n * three\n * four"), [] ), | ||
[ [ "bulletlist", | ||
[ "listitem", | ||
"one", | ||
[ "bulletlist", | ||
[ "listitem", | ||
"two", | ||
[ "bulletlist", | ||
[ "listitem", | ||
"three", | ||
[ "bulletlist", | ||
[ "listitem", "four" ] | ||
] | ||
@@ -268,81 +271,81 @@ ] | ||
] | ||
] ], | ||
"deep nested lists VI") | ||
] | ||
] ], | ||
"deep nested lists VI") | ||
/* Case VII: This one is just fruity! | ||
| * foo | ||
| * bar | ||
| * baz | ||
|* HATE | ||
| * flibble | ||
| * quxx | ||
| * nest? | ||
| * where | ||
| * am | ||
| * i? | ||
*/ | ||
asserts.same( | ||
bl( mk_block(" * foo\n" + | ||
" * bar\n" + | ||
" * baz\n" + | ||
"* HATE\n" + | ||
" * flibble\n" + | ||
" * quxx\n" + | ||
" * nest?\n" + | ||
" * where\n" + | ||
" * am\n" + | ||
" * i?"), | ||
[] ), | ||
[ [ "bulletlist", | ||
[ "listitem", | ||
"foo", | ||
[ "bulletlist", | ||
["listitem", "bar"], | ||
["listitem", "baz"], | ||
["listitem", "HATE"], | ||
["listitem", "flibble"] | ||
] | ||
], | ||
[ "listitem", | ||
"quxx", | ||
[ "bulletlist", | ||
[ "listitem", | ||
"nest?", | ||
[ "bulletlist", | ||
["listitem", "where"], | ||
["listitem", "am"], | ||
["listitem", "i?"] | ||
] | ||
/* Case VII: This one is just fruity! | ||
| * foo | ||
| * bar | ||
| * baz | ||
|* HATE | ||
| * flibble | ||
| * quxx | ||
| * nest? | ||
| * where | ||
| * am | ||
| * i? | ||
*/ | ||
t.equivalent( | ||
bl( mk_block(" * foo\n" + | ||
" * bar\n" + | ||
" * baz\n" + | ||
"* HATE\n" + | ||
" * flibble\n" + | ||
" * quxx\n" + | ||
" * nest?\n" + | ||
" * where\n" + | ||
" * am\n" + | ||
" * i?"), | ||
[] ), | ||
[ [ "bulletlist", | ||
[ "listitem", | ||
"foo", | ||
[ "bulletlist", | ||
["listitem", "bar"], | ||
["listitem", "baz"], | ||
["listitem", "HATE"], | ||
["listitem", "flibble"] | ||
] | ||
], | ||
[ "listitem", | ||
"quxx", | ||
[ "bulletlist", | ||
[ "listitem", | ||
"nest?", | ||
[ "bulletlist", | ||
["listitem", "where"], | ||
["listitem", "am"], | ||
["listitem", "i?"] | ||
] | ||
] | ||
] | ||
] ], | ||
"Indenting Case VII"); | ||
] | ||
] ], | ||
"Indenting Case VII"); | ||
/* Case VIII: Deep nesting + code block | ||
| * one | ||
| * two | ||
| * three | ||
| * four | ||
| | ||
| foo | ||
*/ | ||
asserts.same( | ||
bl( mk_block(" * one\n" + | ||
" 1. two\n" + | ||
" * three\n" + | ||
" * four", | ||
"\n\n"), | ||
[ mk_block(" foo") ] ), | ||
[ [ "bulletlist", | ||
[ "listitem", | ||
["para", "one"], | ||
[ "numberlist", | ||
[ "listitem", | ||
["para", "two"], | ||
[ "bulletlist", | ||
[ "listitem", | ||
[ "para", "three\n * four"], | ||
["code_block", "foo"] | ||
] | ||
/* Case VIII: Deep nesting + code block | ||
| * one | ||
| * two | ||
| * three | ||
| * four | ||
| | ||
| foo | ||
*/ | ||
t.equivalent( | ||
bl( mk_block(" * one\n" + | ||
" 1. two\n" + | ||
" * three\n" + | ||
" * four", | ||
"\n\n"), | ||
[ mk_block(" foo") ] ), | ||
[ [ "bulletlist", | ||
[ "listitem", | ||
["para", "one"], | ||
[ "numberlist", | ||
[ "listitem", | ||
["para", "two"], | ||
[ "bulletlist", | ||
[ "listitem", | ||
[ "para", "three\n * four"], | ||
["code_block", "foo"] | ||
] | ||
@@ -352,161 +355,173 @@ ] | ||
] | ||
] ], | ||
"Case VIII: Deep nesting and code block"); | ||
] | ||
] ], | ||
"Case VIII: Deep nesting and code block"); | ||
}), | ||
}); | ||
test_horizRule: tests.meta(function(md) { | ||
var hr = md.dialect.block.horizRule, | ||
strs = ["---", "_ __", "** ** **", "--- "]; | ||
strs.forEach( function(s) { | ||
asserts.same( | ||
hr.call( md, mk_block(s), [] ), | ||
[ [ "hr" ] ], | ||
"simple hr from " + uneval(s)); | ||
}); | ||
}), | ||
test( "horizRule", function(t, md) { | ||
var hr = md.dialect.block.horizRule, | ||
strs = ["---", "_ __", "** ** **", "--- "]; | ||
strs.forEach( function(s) { | ||
t.equivalent( | ||
hr.call( md, mk_block(s), [] ), | ||
[ [ "hr" ] ], | ||
"simple hr from " + require('util').inspect(s)); | ||
}); | ||
}); | ||
test_blockquote: tests.meta(function(md) { | ||
var bq = md.dialect.block.blockquote; | ||
asserts.same( | ||
bq.call( md, mk_block("> foo\n> bar"), [] ), | ||
[ ["blockquote", ["para", "foo\nbar"] ] ], | ||
"simple blockquote"); | ||
test( "blockquote", function(t, md) { | ||
var bq = md.dialect.block.blockquote; | ||
t.equivalent( | ||
bq.call( md, mk_block("> foo\n> bar"), [] ), | ||
[ ["blockquote", ["para", "foo\nbar"] ] ], | ||
"simple blockquote"); | ||
// Note: this tests horizRule as well through block processing. | ||
asserts.same( | ||
bq.call( md, mk_block("> foo\n> bar\n>\n>- - - "), [] ), | ||
[ ["blockquote", | ||
["para", "foo\nbar"], | ||
["hr"] | ||
] ], | ||
"blockquote with interesting content"); | ||
// Note: this tests horizRule as well through block processing. | ||
t.equivalent( | ||
bq.call( md, mk_block("> foo\n> bar\n>\n>- - - "), [] ), | ||
[ ["blockquote", | ||
["para", "foo\nbar"], | ||
["hr"] | ||
] ], | ||
"blockquote with interesting content"); | ||
}), | ||
}); | ||
test_referenceDefn: tests.meta(function(md) { | ||
var rd = md.dialect.block.referenceDefn; | ||
test( "referenceDefn", function(t, md) { | ||
var rd = md.dialect.block.referenceDefn; | ||
[ '[id]: http://example.com/ "Optional Title Here"', | ||
"[id]: http://example.com/ 'Optional Title Here'", | ||
'[id]: http://example.com/ (Optional Title Here)' | ||
].forEach( function(s) { | ||
md.tree = ["markdown"]; | ||
[ '[id]: http://example.com/ "Optional Title Here"', | ||
"[id]: http://example.com/ 'Optional Title Here'", | ||
'[id]: http://example.com/ (Optional Title Here)' | ||
].forEach( function(s) { | ||
md.tree = ["markdown"]; | ||
asserts.same(rd.call( md, mk_block(s) ), [], "ref processed"); | ||
t.equivalent(rd.call( md, mk_block(s) ), [], "ref processed"); | ||
asserts.same(md.tree[ 1 ].references, | ||
{ "id": { href: "http://example.com/", title: "Optional Title Here" } }, | ||
"reference extracted"); | ||
}); | ||
t.equivalent(md.tree[ 1 ].references, | ||
{ "id": { href: "http://example.com/", title: "Optional Title Here" } }, | ||
"reference extracted"); | ||
}); | ||
// Check a para abbuting a ref works right | ||
md.tree = ["markdown"]; | ||
var next = []; | ||
asserts.same(rd.call( md, mk_block("[id]: example.com\npara"), next ), [], "ref processed"); | ||
asserts.same(md.tree[ 1 ].references, { "id": { href: "example.com" } }, "reference extracted"); | ||
asserts.same(next, [ mk_block("para") ], "paragraph put back into blocks"); | ||
// Check a para abbuting a ref works right | ||
md.tree = ["markdown"]; | ||
var next = []; | ||
t.equivalent(rd.call( md, mk_block("[id]: example.com\npara"), next ), [], "ref processed"); | ||
t.equivalent(md.tree[ 1 ].references, { "id": { href: "example.com" } }, "reference extracted"); | ||
t.equivalent(next, [ mk_block("para") ], "paragraph put back into blocks"); | ||
}), | ||
}); | ||
test_inline_br: tests.meta(function(md) { | ||
asserts.same( | ||
md.processInline("foo \n\\[bar"), | ||
[ "foo", ["linebreak"], "[bar" ], "linebreak+escape"); | ||
}), | ||
test( "inline_br", function(t, md) { | ||
t.equivalent( | ||
md.processInline("foo \n\\[bar"), | ||
[ "foo", ["linebreak"], "[bar" ], "linebreak+escape"); | ||
}); | ||
test_inline_escape: tests.meta(function(md) { | ||
asserts.same( md.processInline("\\bar"), [ "\\bar" ], "invalid escape" ); | ||
asserts.same( md.processInline("\\*foo*"), [ "*foo*" ], "escaped em" ); | ||
}), | ||
test( "inline_escape", function(t, md) { | ||
t.equivalent( md.processInline("\\bar"), [ "\\bar" ], "invalid escape" ); | ||
t.equivalent( md.processInline("\\*foo*"), [ "*foo*" ], "escaped em" ); | ||
}); | ||
test_inline_code: tests.meta(function(md) { | ||
asserts.same( md.processInline("`bar`"), [ ["inlinecode", "bar" ] ], "code I" ); | ||
asserts.same( md.processInline("``b`ar``"), [ ["inlinecode", "b`ar" ] ], "code II" ); | ||
asserts.same( md.processInline("```bar``` baz"), [ ["inlinecode", "bar" ], " baz" ], "code III" ); | ||
}), | ||
test( "inline_code", function(t, md) { | ||
t.equivalent( md.processInline("`bar`"), [ ["inlinecode", "bar" ] ], "code I" ); | ||
t.equivalent( md.processInline("``b`ar``"), [ ["inlinecode", "b`ar" ] ], "code II" ); | ||
t.equivalent( md.processInline("```bar``` baz"), [ ["inlinecode", "bar" ], " baz" ], "code III" ); | ||
}); | ||
test_inline_strong_em: tests.meta(function(md) { | ||
// Yay for horrible edge cases >_< | ||
asserts.same( md.processInline("foo *abc* bar"), [ "foo ", ["em", "abc" ], " bar" ], "strong/em I" ); | ||
asserts.same( md.processInline("*abc `code`"), [ "*abc ", ["inlinecode", "code" ] ], "strong/em II" ); | ||
asserts.same( md.processInline("*abc**def* after"), [ ["em", "abc**def" ], " after" ], "strong/em III" ); | ||
asserts.same( md.processInline("*em **strong * wtf**"), [ ["em", "em **strong " ], " wtf**" ], "strong/em IV" ); | ||
asserts.same( md.processInline("*foo _b*a*r baz"), [ [ "em", "foo _b" ], "a*r baz" ], "strong/em V" ); | ||
}), | ||
test( "inline_strong_em", function(t, md) { | ||
// Yay for horrible edge cases >_< | ||
t.equivalent( md.processInline("foo *abc* bar"), [ "foo ", ["em", "abc" ], " bar" ], "strong/em I" ); | ||
t.equivalent( md.processInline("*abc `code`"), [ "*abc ", ["inlinecode", "code" ] ], "strong/em II" ); | ||
t.equivalent( md.processInline("*abc**def* after"), [ ["em", "abc**def" ], " after" ], "strong/em III" ); | ||
t.equivalent( md.processInline("*em **strong * wtf**"), [ ["em", "em **strong " ], " wtf**" ], "strong/em IV" ); | ||
t.equivalent( md.processInline("*foo _b*a*r baz"), [ [ "em", "foo _b" ], "a*r baz" ], "strong/em V" ); | ||
}); | ||
test_inline_img: tests.meta(function(md) { | ||
test( "inline_img", function(t, md) { | ||
asserts.same( md.processInline( "![alt] (url)" ), | ||
[ [ "img", { href: "url", alt: "alt" } ] ], | ||
"inline img I" ); | ||
t.equivalent( md.processInline( "![alt] (url)" ), | ||
[ [ "img", { href: "url", alt: "alt" } ] ], | ||
"inline img I" ); | ||
asserts.same( md.processInline( "![alt](url 'title')" ), | ||
[ [ "img", { href: "url", alt: "alt", title: "title" } ] ], | ||
"inline img II" ); | ||
t.equivalent( md.processInline( "![alt](url 'title')" ), | ||
[ [ "img", { href: "url", alt: "alt", title: "title" } ] ], | ||
"inline img II" ); | ||
asserts.same( md.processInline( "![alt] (url 'tit'le') after')" ), | ||
[ [ "img", { href: "url", alt: "alt", title: "tit'le" } ], " after')" ], | ||
"inline img III" ); | ||
t.equivalent( md.processInline( "![alt] (url 'tit'le') after')" ), | ||
[ [ "img", { href: "url", alt: "alt", title: "tit'le" } ], " after')" ], | ||
"inline img III" ); | ||
asserts.same( md.processInline( "![alt] (url \"title\")" ), | ||
[ [ "img", { href: "url", alt: "alt", title: "title" } ] ], | ||
"inline img IV" ); | ||
t.equivalent( md.processInline( "![alt] (url \"title\")" ), | ||
[ [ "img", { href: "url", alt: "alt", title: "title" } ] ], | ||
"inline img IV" ); | ||
asserts.same( md.processInline( "![alt][id]" ), | ||
[ [ "img_ref", { ref: "id", alt: "alt", text: "![alt][id]" } ] ], | ||
"ref img I" ); | ||
t.equivalent( md.processInline( '![Alt text](/path/to/img\\\\.jpg "Optional title")' ), | ||
[ [ "img", { href: "/path/to/img\\.jpg", alt: "Alt text", title: "Optional title" } ] ], | ||
"inline img IV" ); | ||
asserts.same( md.processInline( "![alt] [id]" ), | ||
[ [ "img_ref", { ref: "id", alt: "alt", text: "![alt] [id]" } ] ], | ||
"ref img II" ); | ||
}), | ||
t.equivalent( md.processInline( "![alt][id]" ), | ||
[ [ "img_ref", { ref: "id", alt: "alt", original: "![alt][id]" } ] ], | ||
"ref img I" ); | ||
test_inline_link: tests.meta(function(md) { | ||
t.equivalent( md.processInline( "![alt] [id]" ), | ||
[ [ "img_ref", { ref: "id", alt: "alt", original: "![alt] [id]" } ] ], | ||
"ref img II" ); | ||
}); | ||
asserts.same( md.processInline( "[text] (url)" ), | ||
[ [ "link", { href: "url" }, "text" ] ], | ||
"inline link I" ); | ||
test( "inline_link", function(t, md) { | ||
asserts.same( md.processInline( "[text](url 'title')" ), | ||
[ [ "link", { href: "url", title: "title" }, "text" ] ], | ||
"inline link II" ); | ||
t.equivalent( md.processInline( "[text] (url)" ), | ||
[ [ "link", { href: "url" }, "text" ] ], | ||
"inline link I" ); | ||
asserts.same( md.processInline( "[text](url 'tit'le') after')" ), | ||
[ [ "link", { href: "url", title: "tit'le" }, "text" ], " after')" ], | ||
"inline link III" ); | ||
t.equivalent( md.processInline( "[text](url 'title')" ), | ||
[ [ "link", { href: "url", title: "title" }, "text" ] ], | ||
"inline link II" ); | ||
asserts.same( md.processInline( "[text](url \"title\")" ), | ||
[ [ "link", { href: "url", title: "title" }, "text" ] ], | ||
"inline link IV" ); | ||
t.equivalent( md.processInline( "[text](url 'tit'le') after')" ), | ||
[ [ "link", { href: "url", title: "tit'le" }, "text" ], " after')" ], | ||
"inline link III" ); | ||
asserts.same( md.processInline( "[text][id]" ), | ||
[ [ "link_ref", { ref: "id", original: "[text][id]" }, "text" ] ], | ||
"ref link I" ); | ||
t.equivalent( md.processInline( "[text](url \"title\")" ), | ||
[ [ "link", { href: "url", title: "title" }, "text" ] ], | ||
"inline link IV" ); | ||
asserts.same( md.processInline( "[text] [id]" ), | ||
[ [ "link_ref", { ref: "id", original: "[text] [id]" }, "text" ] ], | ||
"ref link II" ); | ||
}), | ||
t.equivalent( md.processInline( "[text][id]" ), | ||
[ [ "link_ref", { ref: "id", original: "[text][id]" }, "text" ] ], | ||
"ref link I" ); | ||
test_inline_autolink: tests.meta(function(md) { | ||
t.equivalent( md.processInline( "[text] [id]" ), | ||
[ [ "link_ref", { ref: "id", original: "[text] [id]" }, "text" ] ], | ||
"ref link II" ); | ||
asserts.same( md.processInline( "<http://foo.com>" ), | ||
[ [ "link", { href: "http://foo.com" }, "http://foo.com" ] ], | ||
"autolink I" ); | ||
t.equivalent( md.processInline( "[to put it another way][SECTION 1] or even [link this](#SECTION-1)" ), | ||
[ | ||
[ "link_ref", | ||
{ ref: "section 1", original: "[to put it another way][SECTION 1]" }, | ||
"to put it another way" | ||
], | ||
" or even ", | ||
[ "link", | ||
{ href: "#SECTION-1" }, | ||
"link this" | ||
], | ||
], | ||
"ref link II" ); | ||
}); | ||
asserts.same( md.processInline( "<mailto:foo@bar.com>" ), | ||
[ [ "link", { href: "mailto:foo@bar.com" }, "foo@bar.com" ] ], | ||
"autolink II" ); | ||
test( "inline_autolink", function(t, md) { | ||
asserts.same( md.processInline( "<foo@bar.com>" ), | ||
[ [ "link", { href: "mailto:foo@bar.com" }, "foo@bar.com" ] ], | ||
"autolink III" ); | ||
}), | ||
} | ||
t.equivalent( md.processInline( "<http://foo.com>" ), | ||
[ [ "link", { href: "http://foo.com" }, "http://foo.com" ] ], | ||
"autolink I" ); | ||
t.equivalent( md.processInline( "<mailto:foo@bar.com>" ), | ||
[ [ "link", { href: "mailto:foo@bar.com" }, "foo@bar.com" ] ], | ||
"autolink II" ); | ||
if (require.main === module) { | ||
var asserts = require('test').asserts; | ||
require('test').runner(tests); | ||
} | ||
t.equivalent( md.processInline( "<foo@bar.com>" ), | ||
[ [ "link", { href: "mailto:foo@bar.com" }, "foo@bar.com" ] ], | ||
"autolink III" ); | ||
}); |
Sorry, the diff of this file is not supported yet
Uses eval
Supply chain riskPackage uses eval() which is a dangerous function. This prevents the code from running in certain environments and increases the risk that the code may contain exploits or malicious behavior.
Found 1 instance in 1 package
488496
834
6755
0