|      1 #!/usr/bin/env lua | 
     1 #!/usr/bin/env lua | 
|      2  | 
     2  | 
|      3 --[[ | 
        | 
|      4 # markdown.lua -- version 0.32 | 
        | 
|      5  | 
        | 
|      6 <http://www.frykholm.se/files/markdown.lua> | 
        | 
|      7  | 
        | 
|      8 **Author:** Niklas Frykholm, <niklas@frykholm.se>   | 
        | 
|      9 **Date:** 31 May 2008 | 
        | 
|     10  | 
        | 
|     11 This is an implementation of the popular text markup language Markdown in pure Lua. | 
        | 
|     12 Markdown can convert documents written in a simple and easy to read text format | 
        | 
|     13 to well-formatted HTML. For a more thourough description of Markdown and the Markdown | 
        | 
|     14 syntax, see <http://daringfireball.net/projects/markdown>. | 
        | 
|     15  | 
        | 
|     16 The original Markdown source is written in Perl and makes heavy use of advanced | 
        | 
|     17 regular expression techniques (such as negative look-ahead, etc) which are not available | 
        | 
|     18 in Lua's simple regex engine. Therefore this Lua port has been rewritten from the ground | 
        | 
|     19 up. It is probably not completely bug free. If you notice any bugs, please report them to | 
        | 
|     20 me. A unit test that exposes the error is helpful. | 
        | 
|     21  | 
        | 
|     22 ## Usage | 
        | 
|     23  | 
        | 
|     24     require "markdown" | 
        | 
|     25     markdown(source) | 
        | 
|     26  | 
        | 
|     27 ``markdown.lua`` exposes a single global function named ``markdown(s)`` which applies the | 
        | 
|     28 Markdown transformation to the specified string. | 
        | 
|     29  | 
        | 
|     30 ``markdown.lua`` can also be used directly from the command line: | 
        | 
|     31  | 
        | 
|     32 	lua markdown.lua test.md | 
        | 
|     33  | 
        | 
|     34 Creates a file ``test.html`` with the converted content of ``test.md``. Run: | 
        | 
|     35  | 
        | 
|     36     lua markdown.lua -h | 
        | 
|     37  | 
        | 
|     38 For a description of the command-line options. | 
        | 
|     39  | 
        | 
|     40 ``markdown.lua`` uses the same license as Lua, the MIT license. | 
        | 
|     41  | 
        | 
|     42 ## License | 
        | 
|     43  | 
        | 
|     44 Copyright © 2008 Niklas Frykholm. | 
        | 
|     45  | 
        | 
|     46 Permission is hereby granted, free of charge, to any person obtaining a copy of this | 
        | 
|     47 software and associated documentation files (the "Software"), to deal in the Software | 
        | 
|     48 without restriction, including without limitation the rights to use, copy, modify, merge, | 
        | 
|     49 publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons | 
        | 
|     50 to whom the Software is furnished to do so, subject to the following conditions:  | 
        | 
|     51  | 
        | 
|     52 The above copyright notice and this permission notice shall be included in all copies | 
        | 
|     53 or substantial portions of the Software.  | 
        | 
|     54  | 
        | 
|     55 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | 
        | 
|     56 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | 
        | 
|     57 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | 
        | 
|     58 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | 
        | 
|     59 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | 
        | 
|     60 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | 
        | 
|     61 THE SOFTWARE. | 
        | 
|     62  | 
        | 
|     63 ## Version history | 
        | 
|     64  | 
        | 
|     65 -	**0.32** -- 31 May 2008 | 
        | 
|     66 	- Fix for links containing brackets | 
        | 
|     67 -	**0.31** -- 1 Mar 2008 | 
        | 
|     68 	-	Fix for link definitions followed by spaces | 
        | 
|     69 -	**0.30** -- 25 Feb 2008 | 
        | 
|     70 	-	Consistent behavior with Markdown when the same link reference is reused | 
        | 
|     71 -	**0.29** -- 24 Feb 2008 | 
        | 
|     72 	-	Fix for <pre> blocks with spaces in them | 
        | 
|     73 -	**0.28** -- 18 Feb 2008 | 
        | 
|     74 	-	Fix for link encoding | 
        | 
|     75 -	**0.27** -- 14 Feb 2008 | 
        | 
|     76 	-	Fix for link database links with () | 
        | 
|     77 -	**0.26** -- 06 Feb 2008 | 
        | 
|     78 	-	Fix for nested italic and bold markers | 
        | 
|     79 -	**0.25** -- 24 Jan 2008 | 
        | 
|     80 	-	Fix for encoding of naked < | 
        | 
|     81 -	**0.24** -- 21 Jan 2008 | 
        | 
|     82 	-	Fix for link behavior. | 
        | 
|     83 -	**0.23** -- 10 Jan 2008 | 
        | 
|     84 	-	Fix for a regression bug in longer expressions in italic or bold. | 
        | 
|     85 -	**0.22** -- 27 Dec 2007 | 
        | 
|     86 	-	Fix for crash when processing blocks with a percent sign in them. | 
        | 
|     87 -	**0.21** -- 27 Dec 2007 | 
        | 
|     88 	- 	Fix for combined strong and emphasis tags | 
        | 
|     89 -	**0.20** -- 13 Oct 2007 | 
        | 
|     90 	-	Fix for < as well in image titles, now matches Dingus behavior | 
        | 
|     91 -	**0.19** -- 28 Sep 2007 | 
        | 
|     92 	-	Fix for quotation marks " and ampersands & in link and image titles. | 
        | 
|     93 -	**0.18** -- 28 Jul 2007 | 
        | 
|     94 	-	Does not crash on unmatched tags (behaves like standard markdown) | 
        | 
|     95 -	**0.17** -- 12 Apr 2007 | 
        | 
|     96 	-	Fix for links with %20 in them. | 
        | 
|     97 -	**0.16** -- 12 Apr 2007 | 
        | 
|     98 	-	Do not require arg global to exist. | 
        | 
|     99 -	**0.15** -- 28 Aug 2006 | 
        | 
|    100 	-	Better handling of links with underscores in them. | 
        | 
|    101 -	**0.14** -- 22 Aug 2006 | 
        | 
|    102 	-	Bug for *`foo()`* | 
        | 
|    103 -	**0.13** -- 12 Aug 2006 | 
        | 
|    104 	-	Added -l option for including stylesheet inline in document. | 
        | 
|    105 	-	Fixed bug in -s flag. | 
        | 
|    106 	-	Fixed emphasis bug. | 
        | 
|    107 -	**0.12** -- 15 May 2006 | 
        | 
|    108 	-	Fixed several bugs to comply with MarkdownTest 1.0 <http://six.pairlist.net/pipermail/markdown-discuss/2004-December/000909.html> | 
        | 
|    109 -	**0.11** -- 12 May 2006 | 
        | 
|    110 	-	Fixed bug for escaping `*` and `_` inside code spans. | 
        | 
|    111 	-	Added license terms. | 
        | 
|    112 	-	Changed join() to table.concat(). | 
        | 
|    113 -	**0.10** -- 3 May 2006 | 
        | 
|    114 	-	Initial public release. | 
        | 
|    115  | 
        | 
|    116 // Niklas | 
        | 
|    117 ]] | 
        | 
|    118  | 
        | 
|    119  | 
        | 
|    120 -- Set up a table for holding local functions to avoid polluting the global namespace | 
        | 
|    121 local M = {} | 
        | 
|    122 local MT = {__index = _G} | 
        | 
|    123 setmetatable(M, MT) | 
        | 
|    124 setfenv(1, M) | 
        | 
|    125  | 
        | 
|    126 ---------------------------------------------------------------------- | 
     3 ---------------------------------------------------------------------- | 
|    127 -- Utility functions | 
     4 -- Utility functions | 
|    128 ---------------------------------------------------------------------- | 
     5 ---------------------------------------------------------------------- | 
|    129  | 
     6  | 
|    130 -- Locks table t from changes, writes an error if someone attempts to change the table. | 
     7 local unpack = table.unpack or unpack | 
|    131 -- This is useful for detecting variables that have "accidently" been made global. Something | 
        | 
|    132 -- I tend to do all too much. | 
        | 
|    133 function lock(t) | 
        | 
|    134 	function lock_new_index(t, k, v) | 
        | 
|    135 		error("module has been locked -- " .. k .. " must be declared local", 2) | 
        | 
|    136 	end | 
        | 
|    137  | 
        | 
|    138 	local mt = {__newindex = lock_new_index} | 
        | 
|    139 	if getmetatable(t) then mt.__index = getmetatable(t).__index end | 
        | 
|    140 	setmetatable(t, mt) | 
        | 
|    141 end | 
        | 
|    142  | 
     8  | 
|    143 -- Returns the result of mapping the values in table t through the function f | 
     9 -- Returns the result of mapping the values in table t through the function f | 
|    144 function map(t, f) | 
    10 local function map(t, f) | 
|    145 	local out = {} | 
    11    local out = {} | 
|    146 	for k,v in pairs(t) do out[k] = f(v,k) end | 
    12    for k,v in pairs(t) do out[k] = f(v,k) end | 
|    147 	return out | 
    13    return out | 
|    148 end | 
    14 end | 
|    149  | 
        | 
|    150 -- The identity function, useful as a placeholder. | 
        | 
|    151 function identity(text) return text end | 
        | 
|    152  | 
    15  | 
|    153 -- Functional style if statement. (NOTE: no short circuit evaluation) | 
    16 -- Functional style if statement. (NOTE: no short circuit evaluation) | 
|    154 function iff(t, a, b) if t then return a else return b end end | 
    17 local function iff(t, a, b) if t then return a else return b end end | 
|    155  | 
    18  | 
|    156 -- Splits the text into an array of separate lines. | 
    19 -- Splits the text into an array of separate lines. | 
|    157 function split(text, sep) | 
    20 local function split(text, sep) | 
|    158 	sep = sep or "\n" | 
    21    sep = sep or "\n" | 
|    159 	local lines = {} | 
    22    local lines = {} | 
|    160 	local pos = 1 | 
    23    local pos = 1 | 
|    161 	while true do | 
    24    while true do | 
|    162 		local b,e = text:find(sep, pos) | 
    25       local b,e = text:find(sep, pos) | 
|    163 		if not b then table.insert(lines, text:sub(pos)) break end | 
    26       if not b then table.insert(lines, text:sub(pos)) break end | 
|    164 		table.insert(lines, text:sub(pos, b-1)) | 
    27       table.insert(lines, text:sub(pos, b-1)) | 
|    165 		pos = e + 1 | 
    28       pos = e + 1 | 
|    166 	end | 
    29    end | 
|    167 	return lines | 
    30    return lines | 
|    168 end | 
    31 end | 
|    169  | 
    32  | 
|    170 -- Converts tabs to spaces | 
    33 -- Converts tabs to spaces | 
|    171 function detab(text) | 
    34 local function detab(text) | 
|    172 	local tab_width = 4 | 
    35    local tab_width = 4 | 
|    173 	local function rep(match) | 
    36    local function rep(match) | 
|    174 		local spaces = -match:len() | 
    37       local spaces = -match:len() | 
|    175 		while spaces<1 do spaces = spaces + tab_width end | 
    38       while spaces<1 do spaces = spaces + tab_width end | 
|    176 		return match .. string.rep(" ", spaces) | 
    39       return match .. string.rep(" ", spaces) | 
|    177 	end | 
    40    end | 
|    178 	text = text:gsub("([^\n]-)\t", rep) | 
    41    text = text:gsub("([^\n]-)\t", rep) | 
|    179 	return text | 
    42    return text | 
|    180 end | 
    43 end | 
|    181  | 
    44  | 
|    182 -- Applies string.find for every pattern in the list and returns the first match | 
    45 -- Applies string.find for every pattern in the list and returns the first match | 
|    183 function find_first(s, patterns, index) | 
    46 local function find_first(s, patterns, index) | 
|    184 	local res = {} | 
    47    local res = {} | 
|    185 	for _,p in ipairs(patterns) do | 
    48    for _,p in ipairs(patterns) do | 
|    186 		local match = {s:find(p, index)} | 
    49       local match = {s:find(p, index)} | 
|    187 		if #match>0 and (#res==0 or match[1] < res[1]) then res = match end | 
    50       if #match>0 and (#res==0 or match[1] < res[1]) then res = match end | 
|    188 	end | 
    51    end | 
|    189 	return table.unpack(res) | 
    52    return unpack(res) | 
|    190 end | 
    53 end | 
|    191  | 
    54  | 
|    192 -- If a replacement array is specified, the range [start, stop] in the array is replaced | 
    55 -- If a replacement array is specified, the range [start, stop] in the array is replaced | 
|    193 -- with the replacement array and the resulting array is returned. Without a replacement | 
    56 -- with the replacement array and the resulting array is returned. Without a replacement | 
|    194 -- array the section of the array between start and stop is returned. | 
    57 -- array the section of the array between start and stop is returned. | 
|    195 function splice(array, start, stop, replacement) | 
    58 local function splice(array, start, stop, replacement) | 
|    196 	if replacement then | 
    59    if replacement then | 
|    197 		local n = stop - start + 1 | 
    60       local n = stop - start + 1 | 
|    198 		while n > 0 do | 
    61       while n > 0 do | 
|    199 			table.remove(array, start) | 
    62          table.remove(array, start) | 
|    200 			n = n - 1 | 
    63          n = n - 1 | 
|    201 		end | 
    64       end | 
|    202 		for i,v in ipairs(replacement) do | 
    65       for _,v in ipairs(replacement) do | 
|    203 			table.insert(array, start, v) | 
    66          table.insert(array, start, v) | 
|    204 		end | 
    67       end | 
|    205 		return array | 
    68       return array | 
|    206 	else | 
    69    else | 
|    207 		local res = {} | 
    70       local res = {} | 
|    208 		for i = start,stop do | 
    71       for i = start,stop do | 
|    209 			table.insert(res, array[i]) | 
    72          table.insert(res, array[i]) | 
|    210 		end | 
    73       end | 
|    211 		return res | 
    74       return res | 
|    212 	end | 
    75    end | 
|    213 end | 
    76 end | 
|    214  | 
    77  | 
|    215 -- Outdents the text one step. | 
    78 -- Outdents the text one step. | 
|    216 function outdent(text) | 
    79 local function outdent(text) | 
|    217 	text = "\n" .. text | 
    80    text = "\n" .. text | 
|    218 	text = text:gsub("\n  ? ? ?", "\n") | 
    81    text = text:gsub("\n  ? ? ?", "\n") | 
|    219 	text = text:sub(2) | 
    82    text = text:sub(2) | 
|    220 	return text | 
    83    return text | 
|    221 end | 
    84 end | 
|    222  | 
    85  | 
|    223 -- Indents the text one step. | 
    86 -- Indents the text one step. | 
|    224 function indent(text) | 
    87 local function indent(text) | 
|    225 	text = text:gsub("\n", "\n    ") | 
    88    text = text:gsub("\n", "\n    ") | 
|    226 	return text | 
    89    return text | 
|    227 end | 
    90 end | 
|    228  | 
    91  | 
|    229 -- Does a simple tokenization of html data. Returns the data as a list of tokens.  | 
    92 -- Does a simple tokenization of html data. Returns the data as a list of tokens. | 
|    230 -- Each token is a table with a type field (which is either "tag" or "text") and | 
    93 -- Each token is a table with a type field (which is either "tag" or "text") and | 
|    231 -- a text field (which contains the original token data). | 
    94 -- a text field (which contains the original token data). | 
|    232 function tokenize_html(html) | 
    95 local function tokenize_html(html) | 
|    233 	local tokens = {} | 
    96    local tokens = {} | 
|    234 	local pos = 1 | 
    97    local pos = 1 | 
|    235 	while true do | 
    98    while true do | 
|    236 		local start = find_first(html, {"<!%-%-", "<[a-z/!$]", "<%?"}, pos) | 
    99       local start = find_first(html, {"<!%-%-", "<[a-z/!$]", "<%?"}, pos) | 
|    237 		if not start then | 
   100       if not start then | 
|    238 			table.insert(tokens, {type="text", text=html:sub(pos)}) | 
   101          table.insert(tokens, {type="text", text=html:sub(pos)}) | 
|    239 			break | 
   102          break | 
|    240 		end | 
   103       end | 
|    241 		if start ~= pos then table.insert(tokens, {type="text", text = html:sub(pos, start-1)}) end | 
   104       if start ~= pos then table.insert(tokens, {type="text", text = html:sub(pos, start-1)}) end | 
|    242 		 | 
   105  | 
|    243 		local _, stop | 
   106       local _, stop | 
|    244 		if html:match("^<!%-%-", start) then | 
   107       if html:match("^<!%-%-", start) then | 
|    245 			_,stop = html:find("%-%->", start) | 
   108          _,stop = html:find("%-%->", start) | 
|    246 		elseif html:match("^<%?", start) then | 
   109       elseif html:match("^<%?", start) then | 
|    247 			_,stop = html:find("?>", start) | 
   110          _,stop = html:find("?>", start) | 
|    248 		else | 
   111       else | 
|    249 			_,stop = html:find("%b<>", start) | 
   112          _,stop = html:find("%b<>", start) | 
|    250 		end | 
   113       end | 
|    251 		if not stop then | 
   114       if not stop then | 
|    252 			-- error("Could not match html tag " .. html:sub(start,start+30))  | 
   115          -- error("Could not match html tag " .. html:sub(start,start+30)) | 
|    253 		 	table.insert(tokens, {type="text", text=html:sub(start, start)}) | 
   116          table.insert(tokens, {type="text", text=html:sub(start, start)}) | 
|    254 			pos = start + 1 | 
   117          pos = start + 1 | 
|    255 		else | 
   118       else | 
|    256 			table.insert(tokens, {type="tag", text=html:sub(start, stop)}) | 
   119          table.insert(tokens, {type="tag", text=html:sub(start, stop)}) | 
|    257 			pos = stop + 1 | 
   120          pos = stop + 1 | 
|    258 		end | 
   121       end | 
|    259 	end | 
   122    end | 
|    260 	return tokens | 
   123    return tokens | 
|    261 end | 
   124 end | 
|    262  | 
   125  | 
|    263 ---------------------------------------------------------------------- | 
   126 ---------------------------------------------------------------------- | 
|    264 -- Hash | 
   127 -- Hash | 
|    265 ---------------------------------------------------------------------- | 
   128 ---------------------------------------------------------------------- | 
|    408 -- characters. | 
   271 -- characters. | 
|    409  | 
   272  | 
|    410 -- Returns true if the line is a ruler of (char) characters. | 
   273 -- Returns true if the line is a ruler of (char) characters. | 
|    411 -- The line must contain at least three char characters and contain only spaces and | 
   274 -- The line must contain at least three char characters and contain only spaces and | 
|    412 -- char characters. | 
   275 -- char characters. | 
|    413 function is_ruler_of(line, char) | 
   276 local function is_ruler_of(line, char) | 
|    414 	if not line:match("^[ %" .. char .. "]*$") then return false end | 
   277    if not line:match("^[ %" .. char .. "]*$") then return false end | 
|    415 	if not line:match("%" .. char .. ".*%" .. char .. ".*%" .. char) then return false end | 
   278    if not line:match("%" .. char .. ".*%" .. char .. ".*%" .. char) then return false end | 
|    416 	return true | 
   279    return true | 
|    417 end | 
   280 end | 
|    418  | 
   281  | 
|    419 -- Identifies the block level formatting present in the line | 
   282 -- Identifies the block level formatting present in the line | 
|    420 function classify(line) | 
   283 local function classify(line) | 
|    421 	local info = {line = line, text = line} | 
   284    local info = {line = line, text = line} | 
|    422 	 | 
   285  | 
|    423 	if line:match("^    ") then | 
   286    if line:match("^    ") then | 
|    424 		info.type = "indented" | 
   287       info.type = "indented" | 
|    425 		info.outdented = line:sub(5) | 
   288       info.outdented = line:sub(5) | 
|    426 		return info | 
   289       return info | 
|    427 	end | 
   290    end | 
|    428 	 | 
   291  | 
|    429 	for _,c in ipairs({'*', '-', '_', '='}) do | 
   292    for _,c in ipairs({'*', '-', '_', '='}) do | 
|    430 		if is_ruler_of(line, c) then | 
   293       if is_ruler_of(line, c) then | 
|    431 			info.type = "ruler" | 
   294          info.type = "ruler" | 
|    432 			info.ruler_char = c | 
   295          info.ruler_char = c | 
|    433 			return info | 
   296          return info | 
|    434 		end | 
   297       end | 
|    435 	end | 
   298    end | 
|    436 	 | 
   299  | 
|    437 	if line == "" then | 
   300    if line == "" then | 
|    438 		info.type = "blank" | 
   301       info.type = "blank" | 
|    439 		return info | 
   302       return info | 
|    440 	end | 
   303    end | 
|    441 	 | 
   304  | 
|    442 	if line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") then | 
   305    if line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") then | 
|    443 		local m1, m2 = line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") | 
   306       local m1, m2 = line:match("^(#+)[ \t]*(.-)[ \t]*#*[ \t]*$") | 
|    444 		info.type = "header" | 
   307       info.type = "header" | 
|    445 		info.level = m1:len() | 
   308       info.level = m1:len() | 
|    446 		info.text = m2 | 
   309       info.text = m2 | 
|    447 		return info | 
   310       return info | 
|    448 	end | 
   311    end | 
|    449 	 | 
   312  | 
|    450 	if line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") then | 
   313    if line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") then | 
|    451 		local number, text = line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") | 
   314       local number, text = line:match("^ ? ? ?(%d+)%.[ \t]+(.+)") | 
|    452 		info.type = "list_item" | 
   315       info.type = "list_item" | 
|    453 		info.list_type = "numeric" | 
   316       info.list_type = "numeric" | 
|    454 		info.number = 0 + number | 
   317       info.number = 0 + number | 
|    455 		info.text = text | 
   318       info.text = text | 
|    456 		return info | 
   319       return info | 
|    457 	end | 
   320    end | 
|    458 	 | 
   321  | 
|    459 	if line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") then | 
   322    if line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") then | 
|    460 		local bullet, text = line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") | 
   323       local bullet, text = line:match("^ ? ? ?([%*%+%-])[ \t]+(.+)") | 
|    461 		info.type = "list_item" | 
   324       info.type = "list_item" | 
|    462 		info.list_type = "bullet" | 
   325       info.list_type = "bullet" | 
|    463 		info.bullet = bullet | 
   326       info.bullet = bullet | 
|    464 		info.text= text | 
   327       info.text= text | 
|    465 		return info | 
   328       return info | 
|    466 	end | 
   329    end | 
|    467 	 | 
   330  | 
|    468 	if line:match("^>[ \t]?(.*)") then | 
   331    if line:match("^>[ \t]?(.*)") then | 
|    469 		info.type = "blockquote" | 
   332       info.type = "blockquote" | 
|    470 		info.text = line:match("^>[ \t]?(.*)") | 
   333       info.text = line:match("^>[ \t]?(.*)") | 
|    471 		return info | 
   334       return info | 
|    472 	end | 
   335    end | 
|    473 	 | 
   336  | 
|    474 	if is_protected(line) then | 
   337    if is_protected(line) then | 
|    475 		info.type = "raw" | 
   338       info.type = "raw" | 
|    476 		info.html = unprotect(line) | 
   339       info.html = unprotect(line) | 
|    477 		return info | 
   340       return info | 
|    478 	end | 
   341    end | 
|    479 	 | 
   342  | 
|    480 	info.type = "normal" | 
   343    info.type = "normal" | 
|    481 	return info | 
   344    return info | 
|    482 end | 
   345 end | 
|    483  | 
   346  | 
|    484 -- Find headers constisting of a normal line followed by a ruler and converts them to | 
   347 -- Find headers constisting of a normal line followed by a ruler and converts them to | 
|    485 -- header entries. | 
   348 -- header entries. | 
|    486 function headers(array) | 
   349 local function headers(array) | 
|    487 	local i = 1 | 
   350    local i = 1 | 
|    488 	while i <= #array - 1 do | 
   351    while i <= #array - 1 do | 
|    489 		if array[i].type  == "normal" and array[i+1].type == "ruler" and  | 
   352       if array[i].type  == "normal" and array[i+1].type == "ruler" and | 
|    490 			(array[i+1].ruler_char == "-" or array[i+1].ruler_char == "=") then | 
   353          (array[i+1].ruler_char == "-" or array[i+1].ruler_char == "=") then | 
|    491 			local info = {line = array[i].line} | 
   354          local info = {line = array[i].line} | 
|    492 			info.text = info.line | 
   355          info.text = info.line | 
|    493 			info.type = "header" | 
   356          info.type = "header" | 
|    494 			info.level = iff(array[i+1].ruler_char == "=", 1, 2) | 
   357          info.level = iff(array[i+1].ruler_char == "=", 1, 2) | 
|    495 			table.remove(array, i+1) | 
   358          table.remove(array, i+1) | 
|    496 			array[i] = info | 
   359          array[i] = info | 
|    497 		end | 
   360       end | 
|    498 		i = i + 1 | 
   361       i = i + 1 | 
|    499 	end | 
   362    end | 
|    500 	return array | 
   363    return array | 
|         | 
   364 end | 
|         | 
   365  | 
|         | 
   366 -- Forward declarations | 
|         | 
   367 local block_transform, span_transform, encode_code | 
|         | 
   368  | 
|         | 
   369 -- Convert lines to html code | 
|         | 
   370 local function blocks_to_html(lines, no_paragraphs) | 
|         | 
   371    local out = {} | 
|         | 
   372    local i = 1 | 
|         | 
   373    while i <= #lines do | 
|         | 
   374       local line = lines[i] | 
|         | 
   375       if line.type == "ruler" then | 
|         | 
   376          table.insert(out, "<hr/>") | 
|         | 
   377       elseif line.type == "raw" then | 
|         | 
   378          table.insert(out, line.html) | 
|         | 
   379       elseif line.type == "normal" then | 
|         | 
   380          local s = line.line | 
|         | 
   381  | 
|         | 
   382          while i+1 <= #lines and lines[i+1].type == "normal" do | 
|         | 
   383             i = i + 1 | 
|         | 
   384             s = s .. "\n" .. lines[i].line | 
|         | 
   385          end | 
|         | 
   386  | 
|         | 
   387          if no_paragraphs then | 
|         | 
   388             table.insert(out, span_transform(s)) | 
|         | 
   389          else | 
|         | 
   390             table.insert(out, "<p>" .. span_transform(s) .. "</p>") | 
|         | 
   391          end | 
|         | 
   392       elseif line.type == "header" then | 
|         | 
   393          local s = "<h" .. line.level .. ">" .. span_transform(line.text) .. "</h" .. line.level .. ">" | 
|         | 
   394          table.insert(out, s) | 
|         | 
   395       else | 
|         | 
   396          table.insert(out, line.line) | 
|         | 
   397       end | 
|         | 
   398       i = i + 1 | 
|         | 
   399    end | 
|         | 
   400    return out | 
|    501 end | 
   401 end | 
|    502  | 
   402  | 
|    503 -- Find list blocks and convert them to protected data blocks | 
   403 -- Find list blocks and convert them to protected data blocks | 
|    504 function lists(array, sublist) | 
   404 local function lists(array, sublist) | 
|    505 	local function process_list(arr) | 
   405    local function process_list(arr) | 
|    506 		local function any_blanks(arr) | 
   406       local function any_blanks(arr) | 
|    507 			for i = 1, #arr do | 
   407          for i = 1, #arr do | 
|    508 				if arr[i].type == "blank" then return true end | 
   408             if arr[i].type == "blank" then return true end | 
|    509 			end | 
   409          end | 
|    510 			return false | 
   410          return false | 
|    511 		end | 
   411       end | 
|    512 		 | 
   412  | 
|    513 		local function split_list_items(arr) | 
   413       local function split_list_items(arr) | 
|    514 			local acc = {arr[1]} | 
   414          local acc = {arr[1]} | 
|    515 			local res = {} | 
   415          local res = {} | 
|    516 			for i=2,#arr do | 
   416          for i=2,#arr do | 
|    517 				if arr[i].type == "list_item" then | 
   417             if arr[i].type == "list_item" then | 
|    518 					table.insert(res, acc) | 
   418                table.insert(res, acc) | 
|    519 					acc = {arr[i]} | 
   419                acc = {arr[i]} | 
|    520 				else | 
   420             else | 
|    521 					table.insert(acc, arr[i]) | 
   421                table.insert(acc, arr[i]) | 
|    522 				end | 
   422             end | 
|    523 			end | 
   423          end | 
|    524 			table.insert(res, acc) | 
   424          table.insert(res, acc) | 
|    525 			return res | 
   425          return res | 
|    526 		end | 
   426       end | 
|    527 		 | 
   427  | 
|    528 		local function process_list_item(lines, block) | 
   428       local function process_list_item(lines, block) | 
|    529 			while lines[#lines].type == "blank" do | 
   429          while lines[#lines].type == "blank" do | 
|    530 				table.remove(lines) | 
   430             table.remove(lines) | 
|    531 			end | 
   431          end | 
|    532 			 | 
   432  | 
|    533 			local itemtext = lines[1].text | 
   433          local itemtext = lines[1].text | 
|    534 			for i=2,#lines do | 
   434          for i=2,#lines do | 
|    535 				itemtext = itemtext .. "\n" .. outdent(lines[i].line) | 
   435             itemtext = itemtext .. "\n" .. outdent(lines[i].line) | 
|    536 			end | 
   436          end | 
|    537 			if block then | 
   437          if block then | 
|    538 				itemtext = block_transform(itemtext, true) | 
   438             itemtext = block_transform(itemtext, true) | 
|    539 				if not itemtext:find("<pre>") then itemtext = indent(itemtext) end | 
   439             if not itemtext:find("<pre>") then itemtext = indent(itemtext) end | 
|    540 				return "    <li>" .. itemtext .. "</li>" | 
   440             return "    <li>" .. itemtext .. "</li>" | 
|    541 			else | 
   441          else | 
|    542 				local lines = split(itemtext) | 
   442             local lines = split(itemtext) | 
|    543 				lines = map(lines, classify) | 
   443             lines = map(lines, classify) | 
|    544 				lines = lists(lines, true) | 
   444             lines = lists(lines, true) | 
|    545 				lines = blocks_to_html(lines, true) | 
   445             lines = blocks_to_html(lines, true) | 
|    546 				itemtext = table.concat(lines, "\n") | 
   446             itemtext = table.concat(lines, "\n") | 
|    547 				if not itemtext:find("<pre>") then itemtext = indent(itemtext) end | 
   447             if not itemtext:find("<pre>") then itemtext = indent(itemtext) end | 
|    548 				return "    <li>" .. itemtext .. "</li>" | 
   448             return "    <li>" .. itemtext .. "</li>" | 
|    549 			end | 
   449          end | 
|    550 		end | 
   450       end | 
|    551 		 | 
   451  | 
|    552 		local block_list = any_blanks(arr) | 
   452       local block_list = any_blanks(arr) | 
|    553 		local items = split_list_items(arr) | 
   453       local items = split_list_items(arr) | 
|    554 		local out = "" | 
   454       local out = "" | 
|    555 		for _, item in ipairs(items) do | 
   455       for _, item in ipairs(items) do | 
|    556 			out = out .. process_list_item(item, block_list) .. "\n" | 
   456          out = out .. process_list_item(item, block_list) .. "\n" | 
|    557 		end | 
   457       end | 
|    558 		if arr[1].list_type == "numeric" then | 
   458       if arr[1].list_type == "numeric" then | 
|    559 			return "<ol>\n" .. out .. "</ol>" | 
   459          return "<ol>\n" .. out .. "</ol>" | 
|    560 		else | 
   460       else | 
|    561 			return "<ul>\n" .. out .. "</ul>" | 
   461          return "<ul>\n" .. out .. "</ul>" | 
|    562 		end | 
   462       end | 
|    563 	end | 
   463    end | 
|    564 	 | 
   464  | 
|    565 	-- Finds the range of lines composing the first list in the array. A list | 
   465    -- Finds the range of lines composing the first list in the array. A list | 
|    566 	-- starts with (^ list_item) or (blank list_item) and ends with | 
   466    -- starts with (^ list_item) or (blank list_item) and ends with | 
|    567 	-- (blank* $) or (blank normal). | 
   467    -- (blank* $) or (blank normal). | 
|    568 	-- | 
   468    -- | 
|    569 	-- A sublist can start with just (list_item) does not need a blank... | 
   469    -- A sublist can start with just (list_item) does not need a blank... | 
|    570 	local function find_list(array, sublist) | 
   470    local function find_list(array, sublist) | 
|    571 		local function find_list_start(array, sublist) | 
   471       local function find_list_start(array, sublist) | 
|    572 			if array[1].type == "list_item" then return 1 end | 
   472          if array[1].type == "list_item" then return 1 end | 
|    573 			if sublist then | 
   473          if sublist then | 
|    574 				for i = 1,#array do | 
   474             for i = 1,#array do | 
|    575 					if array[i].type == "list_item" then return i end | 
   475                if array[i].type == "list_item" then return i end | 
|    576 				end | 
   476             end | 
|    577 			else | 
   477          else | 
|    578 				for i = 1, #array-1 do | 
   478             for i = 1, #array-1 do | 
|    579 					if array[i].type == "blank" and array[i+1].type == "list_item" then | 
   479                if array[i].type == "blank" and array[i+1].type == "list_item" then | 
|    580 						return i+1 | 
   480                   return i+1 | 
|    581 					end | 
   481                end | 
|    582 				end | 
   482             end | 
|    583 			end | 
   483          end | 
|    584 			return nil | 
   484          return nil | 
|    585 		end | 
   485       end | 
|    586 		local function find_list_end(array, start) | 
   486       local function find_list_end(array, start) | 
|    587 			local pos = #array | 
   487          local pos = #array | 
|    588 			for i = start, #array-1 do | 
   488          for i = start, #array-1 do | 
|    589 				if array[i].type == "blank" and array[i+1].type ~= "list_item"  | 
   489             if array[i].type == "blank" and array[i+1].type ~= "list_item" | 
|    590 					and array[i+1].type ~= "indented" and array[i+1].type ~= "blank" then | 
   490                and array[i+1].type ~= "indented" and array[i+1].type ~= "blank" then | 
|    591 					pos = i-1 | 
   491                pos = i-1 | 
|    592 					break | 
   492                break | 
|    593 				end | 
   493             end | 
|    594 			end | 
   494          end | 
|    595 			while pos > start and array[pos].type == "blank" do | 
   495          while pos > start and array[pos].type == "blank" do | 
|    596 				pos = pos - 1 | 
   496             pos = pos - 1 | 
|    597 			end | 
   497          end | 
|    598 			return pos | 
   498          return pos | 
|    599 		end | 
   499       end | 
|    600 		 | 
   500  | 
|    601 		local start = find_list_start(array, sublist) | 
   501       local start = find_list_start(array, sublist) | 
|    602 		if not start then return nil end | 
   502       if not start then return nil end | 
|    603 		return start, find_list_end(array, start) | 
   503       return start, find_list_end(array, start) | 
|    604 	end | 
   504    end | 
|    605 	 | 
   505  | 
|    606 	while true do | 
   506    while true do | 
|    607 		local start, stop = find_list(array, sublist) | 
   507       local start, stop = find_list(array, sublist) | 
|    608 		if not start then break end | 
   508       if not start then break end | 
|    609 		local text = process_list(splice(array, start, stop)) | 
   509       local text = process_list(splice(array, start, stop)) | 
|    610 		local info = { | 
   510       local info = { | 
|    611 			line = text, | 
   511          line = text, | 
|    612 			type = "raw", | 
   512          type = "raw", | 
|    613 			html = text | 
   513          html = text | 
|    614 		} | 
   514       } | 
|    615 		array = splice(array, start, stop, {info}) | 
   515       array = splice(array, start, stop, {info}) | 
|    616 	end | 
   516    end | 
|    617 	 | 
   517  | 
|    618 	-- Convert any remaining list items to normal | 
   518    -- Convert any remaining list items to normal | 
|    619 	for _,line in ipairs(array) do | 
   519    for _,line in ipairs(array) do | 
|    620 		if line.type == "list_item" then line.type = "normal" end | 
   520       if line.type == "list_item" then line.type = "normal" end | 
|    621 	end | 
   521    end | 
|    622 	 | 
   522  | 
|    623 	return array | 
   523    return array | 
|    624 end | 
   524 end | 
|    625  | 
   525  | 
|    626 -- Find and convert blockquote markers. | 
   526 -- Find and convert blockquote markers. | 
|    627 function blockquotes(lines) | 
   527 local function blockquotes(lines) | 
|    628 	local function find_blockquote(lines) | 
   528    local function find_blockquote(lines) | 
|    629 		local start | 
   529       local start | 
|    630 		for i,line in ipairs(lines) do | 
   530       for i,line in ipairs(lines) do | 
|    631 			if line.type == "blockquote" then | 
   531          if line.type == "blockquote" then | 
|    632 				start = i | 
   532             start = i | 
|    633 				break | 
   533             break | 
|    634 			end | 
   534          end | 
|    635 		end | 
   535       end | 
|    636 		if not start then return nil end | 
   536       if not start then return nil end | 
|    637 		 | 
   537  | 
|    638 		local stop = #lines | 
   538       local stop = #lines | 
|    639 		for i = start+1, #lines do | 
   539       for i = start+1, #lines do | 
|    640 			if lines[i].type == "blank" or lines[i].type == "blockquote" then | 
   540          if lines[i].type == "blank" or lines[i].type == "blockquote" then | 
|    641 			elseif lines[i].type == "normal" then | 
   541          elseif lines[i].type == "normal" then | 
|    642 				if lines[i-1].type == "blank" then stop = i-1 break end | 
   542             if lines[i-1].type == "blank" then stop = i-1 break end | 
|    643 			else | 
   543          else | 
|    644 				stop = i-1 break | 
   544             stop = i-1 break | 
|    645 			end | 
   545          end | 
|    646 		end | 
   546       end | 
|    647 		while lines[stop].type == "blank" do stop = stop - 1 end | 
   547       while lines[stop].type == "blank" do stop = stop - 1 end | 
|    648 		return start, stop | 
   548       return start, stop | 
|    649 	end | 
   549    end | 
|    650 	 | 
   550  | 
|    651 	local function process_blockquote(lines) | 
   551    local function process_blockquote(lines) | 
|    652 		local raw = lines[1].text | 
   552       local raw = lines[1].text | 
|    653 		for i = 2,#lines do | 
   553       for i = 2,#lines do | 
|    654 			raw = raw .. "\n" .. lines[i].text | 
   554          raw = raw .. "\n" .. lines[i].text | 
|    655 		end | 
   555       end | 
|    656 		local bt = block_transform(raw) | 
   556       local bt = block_transform(raw) | 
|    657 		if not bt:find("<pre>") then bt = indent(bt) end | 
   557       if not bt:find("<pre>") then bt = indent(bt) end | 
|    658 		return "<blockquote>\n    " .. bt .. | 
   558       return "<blockquote>\n    " .. bt .. | 
|    659 			"\n</blockquote>" | 
   559          "\n</blockquote>" | 
|    660 	end | 
   560    end | 
|    661 	 | 
   561  | 
|    662 	while true do | 
   562    while true do | 
|    663 		local start, stop = find_blockquote(lines) | 
   563       local start, stop = find_blockquote(lines) | 
|    664 		if not start then break end | 
   564       if not start then break end | 
|    665 		local text = process_blockquote(splice(lines, start, stop)) | 
   565       local text = process_blockquote(splice(lines, start, stop)) | 
|    666 		local info = { | 
   566       local info = { | 
|    667 			line = text, | 
   567          line = text, | 
|    668 			type = "raw", | 
   568          type = "raw", | 
|    669 			html = text | 
   569          html = text | 
|    670 		} | 
   570       } | 
|    671 		lines = splice(lines, start, stop, {info}) | 
   571       lines = splice(lines, start, stop, {info}) | 
|    672 	end | 
   572    end | 
|    673 	return lines | 
   573    return lines | 
|    674 end | 
   574 end | 
|    675  | 
   575  | 
|    676 -- Find and convert codeblocks. | 
   576 -- Find and convert codeblocks. | 
|    677 function codeblocks(lines) | 
   577 local function codeblocks(lines) | 
|    678 	local function find_codeblock(lines) | 
   578    local function find_codeblock(lines) | 
|    679 		local start | 
   579       local start | 
|    680 		for i,line in ipairs(lines) do | 
   580       for i,line in ipairs(lines) do | 
|    681 			if line.type == "indented" then start = i break end | 
   581          if line.type == "indented" then start = i break end | 
|    682 		end | 
   582       end | 
|    683 		if not start then return nil end | 
   583       if not start then return nil end | 
|    684 		 | 
   584  | 
|    685 		local stop = #lines | 
   585       local stop = #lines | 
|    686 		for i = start+1, #lines do | 
   586       for i = start+1, #lines do | 
|    687 			if lines[i].type ~= "indented" and lines[i].type ~= "blank" then | 
   587          if lines[i].type ~= "indented" and lines[i].type ~= "blank" then | 
|    688 				stop = i-1 | 
   588             stop = i-1 | 
|    689 				break | 
   589             break | 
|    690 			end | 
   590          end | 
|    691 		end | 
   591       end | 
|    692 		while lines[stop].type == "blank" do stop = stop - 1 end | 
   592       while lines[stop].type == "blank" do stop = stop - 1 end | 
|    693 		return start, stop | 
   593       return start, stop | 
|    694 	end | 
   594    end | 
|    695 	 | 
   595  | 
|    696 	local function process_codeblock(lines) | 
   596    local function process_codeblock(lines) | 
|    697 		local raw = detab(encode_code(outdent(lines[1].line))) | 
   597       local raw = detab(encode_code(outdent(lines[1].line))) | 
|    698 		for i = 2,#lines do | 
   598       for i = 2,#lines do | 
|    699 			raw = raw .. "\n" .. detab(encode_code(outdent(lines[i].line))) | 
   599          raw = raw .. "\n" .. detab(encode_code(outdent(lines[i].line))) | 
|    700 		end | 
   600       end | 
|    701 		return "<pre><code>" .. raw .. "\n</code></pre>" | 
   601       return "<pre><code>" .. raw .. "\n</code></pre>" | 
|    702 	end | 
   602    end | 
|    703 	 | 
   603  | 
|    704 	while true do | 
   604    while true do | 
|    705 		local start, stop = find_codeblock(lines) | 
   605       local start, stop = find_codeblock(lines) | 
|    706 		if not start then break end | 
   606       if not start then break end | 
|    707 		local text = process_codeblock(splice(lines, start, stop)) | 
   607       local text = process_codeblock(splice(lines, start, stop)) | 
|    708 		local info = { | 
   608       local info = { | 
|    709 			line = text, | 
   609          line = text, | 
|    710 			type = "raw", | 
   610          type = "raw", | 
|    711 			html = text | 
   611          html = text | 
|    712 		} | 
   612       } | 
|    713 		lines = splice(lines, start, stop, {info}) | 
   613       lines = splice(lines, start, stop, {info}) | 
|    714 	end | 
   614    end | 
|    715 	return lines | 
   615    return lines | 
|    716 end | 
        | 
|    717  | 
        | 
|    718 -- Convert lines to html code | 
        | 
|    719 function blocks_to_html(lines, no_paragraphs) | 
        | 
|    720 	local out = {} | 
        | 
|    721 	local i = 1 | 
        | 
|    722 	while i <= #lines do | 
        | 
|    723 		local line = lines[i] | 
        | 
|    724 		if line.type == "ruler" then | 
        | 
|    725 			table.insert(out, "<hr/>") | 
        | 
|    726 		elseif line.type == "raw" then | 
        | 
|    727 			table.insert(out, line.html) | 
        | 
|    728 		elseif line.type == "normal" then | 
        | 
|    729 			local s = line.line | 
        | 
|    730 			 | 
        | 
|    731 			while i+1 <= #lines and lines[i+1].type == "normal" do | 
        | 
|    732 				i = i + 1 | 
        | 
|    733 				s = s .. "\n" .. lines[i].line | 
        | 
|    734 			end | 
        | 
|    735 			 | 
        | 
|    736 			if no_paragraphs then | 
        | 
|    737 				table.insert(out, span_transform(s)) | 
        | 
|    738 			else | 
        | 
|    739 				table.insert(out, "<p>" .. span_transform(s) .. "</p>") | 
        | 
|    740 			end | 
        | 
|    741 		elseif line.type == "header" then | 
        | 
|    742 			local s = "<h" .. line.level .. ">" .. span_transform(line.text) .. "</h" .. line.level .. ">" | 
        | 
|    743 			table.insert(out, s) | 
        | 
|    744 		else | 
        | 
|    745 			table.insert(out, line.line) | 
        | 
|    746 		end | 
        | 
|    747 		i = i + 1 | 
        | 
|    748 	end | 
        | 
|    749 	return out | 
        | 
|    750 end | 
   616 end | 
|    751  | 
   617  | 
|    752 -- Perform all the block level transforms | 
   618 -- Perform all the block level transforms | 
|    753 function block_transform(text, sublist) | 
   619 function block_transform(text, sublist) | 
|    754 	local lines = split(text) | 
   620    local lines = split(text) | 
|    755 	lines = map(lines, classify) | 
   621    lines = map(lines, classify) | 
|    756 	lines = headers(lines) | 
   622    lines = headers(lines) | 
|    757 	lines = lists(lines, sublist) | 
   623    lines = lists(lines, sublist) | 
|    758 	lines = codeblocks(lines) | 
   624    lines = codeblocks(lines) | 
|    759 	lines = blockquotes(lines) | 
   625    lines = blockquotes(lines) | 
|    760 	lines = blocks_to_html(lines) | 
   626    lines = blocks_to_html(lines) | 
|    761 	local text = table.concat(lines, "\n") | 
   627    local text = table.concat(lines, "\n") | 
|    762 	return text | 
   628    return text | 
|    763 end | 
        | 
|    764  | 
        | 
|    765 -- Debug function for printing a line array to see the result | 
        | 
|    766 -- of partial transforms. | 
        | 
|    767 function print_lines(lines) | 
        | 
|    768 	for i, line in ipairs(lines) do | 
        | 
|    769 		print(i, line.type, line.text or line.line) | 
        | 
|    770 	end | 
        | 
|    771 end | 
   629 end | 
|    772  | 
   630  | 
|    773 ---------------------------------------------------------------------- | 
   631 ---------------------------------------------------------------------- | 
|    774 -- Span transform | 
   632 -- Span transform | 
|    775 ---------------------------------------------------------------------- | 
   633 ---------------------------------------------------------------------- | 
|    776  | 
   634  | 
|    777 -- Functions for transforming the text at the span level. | 
   635 -- Functions for transforming the text at the span level. | 
|    778  | 
   636  | 
|    779 -- These characters may need to be escaped because they have a special | 
   637 -- These characters may need to be escaped because they have a special | 
|    780 -- meaning in markdown. | 
   638 -- meaning in markdown. | 
|    781 escape_chars = "'\\`*_{}[]()>#+-.!'" | 
   639 local escape_chars = "'\\`*_{}[]()>#+-.!'" | 
|    782 escape_table = {} | 
   640 local escape_table = {} | 
|    783  | 
   641  | 
|    784 function init_escape_table() | 
   642 local function init_escape_table() | 
|    785 	escape_table = {} | 
   643    escape_table = {} | 
|    786 	for i = 1,#escape_chars do | 
   644    for i = 1,#escape_chars do | 
|    787 		local c = escape_chars:sub(i,i) | 
   645       local c = escape_chars:sub(i,i) | 
|    788 		escape_table[c] = hash(c) | 
   646       escape_table[c] = hash(c) | 
|    789 	end | 
   647    end | 
|    790 end | 
   648 end | 
|    791  | 
   649  | 
|    792 -- Adds a new escape to the escape table. | 
   650 -- Adds a new escape to the escape table. | 
|    793 function add_escape(text) | 
   651 local function add_escape(text) | 
|    794 	if not escape_table[text] then | 
   652    if not escape_table[text] then | 
|    795 		escape_table[text] = hash(text) | 
   653       escape_table[text] = hash(text) | 
|    796 	end | 
   654    end | 
|    797 	return escape_table[text] | 
   655    return escape_table[text] | 
|    798 end	 | 
   656 end | 
|         | 
   657  | 
|         | 
   658 -- Encode backspace-escaped characters in the markdown source. | 
|         | 
   659 local function encode_backslash_escapes(t) | 
|         | 
   660    for i=1,escape_chars:len() do | 
|         | 
   661       local c = escape_chars:sub(i,i) | 
|         | 
   662       t = t:gsub("\\%" .. c, escape_table[c]) | 
|         | 
   663    end | 
|         | 
   664    return t | 
|         | 
   665 end | 
|    799  | 
   666  | 
|    800 -- Escape characters that should not be disturbed by markdown. | 
   667 -- Escape characters that should not be disturbed by markdown. | 
|    801 function escape_special_chars(text) | 
   668 local function escape_special_chars(text) | 
|    802 	local tokens = tokenize_html(text) | 
   669    local tokens = tokenize_html(text) | 
|    803 	 | 
   670  | 
|    804 	local out = "" | 
   671    local out = "" | 
|    805 	for _, token in ipairs(tokens) do | 
   672    for _, token in ipairs(tokens) do | 
|    806 		local t = token.text | 
   673       local t = token.text | 
|    807 		if token.type == "tag" then | 
   674       if token.type == "tag" then | 
|    808 			-- In tags, encode * and _ so they don't conflict with their use in markdown. | 
   675          -- In tags, encode * and _ so they don't conflict with their use in markdown. | 
|    809 			t = t:gsub("%*", escape_table["*"]) | 
   676          t = t:gsub("%*", escape_table["*"]) | 
|    810 			t = t:gsub("%_", escape_table["_"]) | 
   677          t = t:gsub("%_", escape_table["_"]) | 
|    811 		else | 
   678       else | 
|    812 			t = encode_backslash_escapes(t) | 
   679          t = encode_backslash_escapes(t) | 
|    813 		end | 
   680       end | 
|    814 		out = out .. t | 
   681       out = out .. t | 
|    815 	end | 
   682    end | 
|    816 	return out | 
   683    return out | 
|    817 end | 
        | 
|    818  | 
        | 
|    819 -- Encode backspace-escaped characters in the markdown source. | 
        | 
|    820 function encode_backslash_escapes(t) | 
        | 
|    821 	for i=1,escape_chars:len() do | 
        | 
|    822 		local c = escape_chars:sub(i,i) | 
        | 
|    823 		t = t:gsub("\\%" .. c, escape_table[c]) | 
        | 
|    824 	end | 
        | 
|    825 	return t | 
        | 
|    826 end | 
   684 end | 
|    827  | 
   685  | 
|    828 -- Unescape characters that have been encoded. | 
   686 -- Unescape characters that have been encoded. | 
|    829 function unescape_special_chars(t) | 
   687 local function unescape_special_chars(t) | 
|    830 	local tin = t | 
   688    local tin = t | 
|    831 	for k,v in pairs(escape_table) do | 
   689    for k,v in pairs(escape_table) do | 
|    832 		k = k:gsub("%%", "%%%%") | 
   690       k = k:gsub("%%", "%%%%") | 
|    833 		t = t:gsub(v,k) | 
   691       t = t:gsub(v,k) | 
|    834 	end | 
   692    end | 
|    835 	if t ~= tin then t = unescape_special_chars(t) end | 
   693    if t ~= tin then t = unescape_special_chars(t) end | 
|    836 	return t | 
   694    return t | 
|    837 end | 
   695 end | 
|    838  | 
   696  | 
|    839 -- Encode/escape certain characters inside Markdown code runs. | 
   697 -- Encode/escape certain characters inside Markdown code runs. | 
|    840 -- The point is that in code, these characters are literals, | 
   698 -- The point is that in code, these characters are literals, | 
|    841 -- and lose their special Markdown meanings. | 
   699 -- and lose their special Markdown meanings. | 
|    842 function encode_code(s) | 
   700 function encode_code(s) | 
|    843 	s = s:gsub("%&", "&") | 
   701    s = s:gsub("%&", "&") | 
|    844 	s = s:gsub("<", "<") | 
   702    s = s:gsub("<", "<") | 
|    845 	s = s:gsub(">", ">") | 
   703    s = s:gsub(">", ">") | 
|    846 	for k,v in pairs(escape_table) do | 
   704    for k,v in pairs(escape_table) do | 
|    847 		s = s:gsub("%"..k, v) | 
   705       s = s:gsub("%"..k, v) | 
|    848 	end | 
   706    end | 
|    849 	return s | 
   707    return s | 
|    850 end | 
   708 end | 
|    851  | 
   709  | 
|    852 -- Handle backtick blocks. | 
   710 -- Handle backtick blocks. | 
|    853 function code_spans(s) | 
   711 local function code_spans(s) | 
|    854 	s = s:gsub("\\\\", escape_table["\\"]) | 
   712    s = s:gsub("\\\\", escape_table["\\"]) | 
|    855 	s = s:gsub("\\`", escape_table["`"]) | 
   713    s = s:gsub("\\`", escape_table["`"]) | 
|    856  | 
   714  | 
|    857 	local pos = 1 | 
   715    local pos = 1 | 
|    858 	while true do | 
   716    while true do | 
|    859 		local start, stop = s:find("`+", pos) | 
   717       local start, stop = s:find("`+", pos) | 
|    860 		if not start then return s end | 
   718       if not start then return s end | 
|    861 		local count = stop - start + 1 | 
   719       local count = stop - start + 1 | 
|    862 		-- Find a matching numbert of backticks | 
   720       -- Find a matching numbert of backticks | 
|    863 		local estart, estop = s:find(string.rep("`", count), stop+1) | 
   721       local estart, estop = s:find(string.rep("`", count), stop+1) | 
|    864 		local brstart = s:find("\n", stop+1) | 
   722       local brstart = s:find("\n", stop+1) | 
|    865 		if estart and (not brstart or estart < brstart) then | 
   723       if estart and (not brstart or estart < brstart) then | 
|    866 			local code = s:sub(stop+1, estart-1) | 
   724          local code = s:sub(stop+1, estart-1) | 
|    867 			code = code:gsub("^[ \t]+", "") | 
   725          code = code:gsub("^[ \t]+", "") | 
|    868 			code = code:gsub("[ \t]+$", "") | 
   726          code = code:gsub("[ \t]+$", "") | 
|    869 			code = code:gsub(escape_table["\\"], escape_table["\\"] .. escape_table["\\"]) | 
   727          code = code:gsub(escape_table["\\"], escape_table["\\"] .. escape_table["\\"]) | 
|    870 			code = code:gsub(escape_table["`"], escape_table["\\"] .. escape_table["`"]) | 
   728          code = code:gsub(escape_table["`"], escape_table["\\"] .. escape_table["`"]) | 
|    871 			code = "<code>" .. encode_code(code) .. "</code>" | 
   729          code = "<code>" .. encode_code(code) .. "</code>" | 
|    872 			code = add_escape(code) | 
   730          code = add_escape(code) | 
|    873 			s = s:sub(1, start-1) .. code .. s:sub(estop+1) | 
   731          s = s:sub(1, start-1) .. code .. s:sub(estop+1) | 
|    874 			pos = start + code:len() | 
   732          pos = start + code:len() | 
|    875 		else | 
   733       else | 
|    876 			pos = stop + 1 | 
   734          pos = stop + 1 | 
|    877 		end | 
   735       end | 
|    878 	end | 
   736    end | 
|    879 	return s | 
   737    return s | 
|    880 end | 
   738 end | 
|    881  | 
   739  | 
|    882 -- Encode alt text... enodes &, and ". | 
   740 -- Encode alt text... enodes &, and ". | 
|    883 function encode_alt(s) | 
   741 local function encode_alt(s) | 
|    884 	if not s then return s end | 
   742    if not s then return s end | 
|    885 	s = s:gsub('&', '&') | 
   743    s = s:gsub('&', '&') | 
|    886 	s = s:gsub('"', '"') | 
   744    s = s:gsub('"', '"') | 
|    887 	s = s:gsub('<', '<') | 
   745    s = s:gsub('<', '<') | 
|    888 	return s | 
   746    return s | 
|    889 end | 
   747 end | 
|         | 
   748  | 
|         | 
   749 -- Forward declaration for link_db as returned by strip_link_definitions. | 
|         | 
   750 local link_database | 
|    890  | 
   751  | 
|    891 -- Handle image references | 
   752 -- Handle image references | 
|    892 function images(text) | 
   753 local function images(text) | 
|    893 	local function reference_link(alt, id) | 
   754    local function reference_link(alt, id) | 
|    894 		alt = encode_alt(alt:match("%b[]"):sub(2,-2)) | 
   755       alt = encode_alt(alt:match("%b[]"):sub(2,-2)) | 
|    895 		id = id:match("%[(.*)%]"):lower() | 
   756       id = id:match("%[(.*)%]"):lower() | 
|    896 		if id == "" then id = text:lower() end | 
   757       if id == "" then id = text:lower() end | 
|    897 		link_database[id] = link_database[id] or {} | 
   758       link_database[id] = link_database[id] or {} | 
|    898 		if not link_database[id].url then return nil end | 
   759       if not link_database[id].url then return nil end | 
|    899 		local url = link_database[id].url or id | 
   760       local url = link_database[id].url or id | 
|    900 		url = encode_alt(url) | 
   761       url = encode_alt(url) | 
|    901 		local title = encode_alt(link_database[id].title) | 
   762       local title = encode_alt(link_database[id].title) | 
|    902 		if title then title = " title=\"" .. title .. "\"" else title = "" end | 
   763       if title then title = " title=\"" .. title .. "\"" else title = "" end | 
|    903 		return add_escape ('<img src="' .. url .. '" alt="' .. alt .. '"' .. title .. "/>") | 
   764       return add_escape ('<img src="' .. url .. '" alt="' .. alt .. '"' .. title .. "/>") | 
|    904 	end | 
   765    end | 
|    905 	 | 
   766  | 
|    906 	local function inline_link(alt, link) | 
   767    local function inline_link(alt, link) | 
|    907 		alt = encode_alt(alt:match("%b[]"):sub(2,-2)) | 
   768       alt = encode_alt(alt:match("%b[]"):sub(2,-2)) | 
|    908 		local url, title = link:match("%(<?(.-)>?[ \t]*['\"](.+)['\"]") | 
   769       local url, title = link:match("%(<?(.-)>?[ \t]*['\"](.+)['\"]") | 
|    909 		url = url or link:match("%(<?(.-)>?%)") | 
   770       url = url or link:match("%(<?(.-)>?%)") | 
|    910 		url = encode_alt(url) | 
   771       url = encode_alt(url) | 
|    911 		title = encode_alt(title) | 
   772       title = encode_alt(title) | 
|    912 		if title then | 
   773       if title then | 
|    913 			return add_escape('<img src="' .. url .. '" alt="' .. alt .. '" title="' .. title .. '"/>') | 
   774          return add_escape('<img src="' .. url .. '" alt="' .. alt .. '" title="' .. title .. '"/>') | 
|    914 		else | 
   775       else | 
|    915 			return add_escape('<img src="' .. url .. '" alt="' .. alt .. '"/>') | 
   776          return add_escape('<img src="' .. url .. '" alt="' .. alt .. '"/>') | 
|    916 		end | 
   777       end | 
|    917 	end | 
   778    end | 
|    918 	 | 
   779  | 
|    919 	text = text:gsub("!(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) | 
   780    text = text:gsub("!(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) | 
|    920 	text = text:gsub("!(%b[])(%b())", inline_link) | 
   781    text = text:gsub("!(%b[])(%b())", inline_link) | 
|    921 	return text | 
   782    return text | 
|    922 end | 
   783 end | 
|    923  | 
   784  | 
|    924 -- Handle anchor references | 
   785 -- Handle anchor references | 
|    925 function anchors(text) | 
   786 local function anchors(text) | 
|    926 	local function reference_link(text, id) | 
   787    local function reference_link(text, id) | 
|    927 		text = text:match("%b[]"):sub(2,-2) | 
   788       text = text:match("%b[]"):sub(2,-2) | 
|    928 		id = id:match("%b[]"):sub(2,-2):lower() | 
   789       id = id:match("%b[]"):sub(2,-2):lower() | 
|    929 		if id == "" then id = text:lower() end | 
   790       if id == "" then id = text:lower() end | 
|    930 		link_database[id] = link_database[id] or {} | 
   791       link_database[id] = link_database[id] or {} | 
|    931 		if not link_database[id].url then return nil end | 
   792       if not link_database[id].url then return nil end | 
|    932 		local url = link_database[id].url or id | 
   793       local url = link_database[id].url or id | 
|    933 		url = encode_alt(url) | 
   794       url = encode_alt(url) | 
|    934 		local title = encode_alt(link_database[id].title) | 
   795       local title = encode_alt(link_database[id].title) | 
|    935 		if title then title = " title=\"" .. title .. "\"" else title = "" end | 
   796       if title then title = " title=\"" .. title .. "\"" else title = "" end | 
|    936 		return add_escape("<a href=\"" .. url .. "\"" .. title .. ">") .. text .. add_escape("</a>") | 
   797       return add_escape("<a href=\"" .. url .. "\"" .. title .. ">") .. text .. add_escape("</a>") | 
|    937 	end | 
   798    end | 
|    938 	 | 
   799  | 
|    939 	local function inline_link(text, link) | 
   800    local function inline_link(text, link) | 
|    940 		text = text:match("%b[]"):sub(2,-2) | 
   801       text = text:match("%b[]"):sub(2,-2) | 
|    941 		local url, title = link:match("%(<?(.-)>?[ \t]*['\"](.+)['\"]") | 
   802       local url, title = link:match("%(<?(.-)>?[ \t]*['\"](.+)['\"]") | 
|    942 		title = encode_alt(title) | 
   803       title = encode_alt(title) | 
|    943 		url  = url or  link:match("%(<?(.-)>?%)") or "" | 
   804       url  = url or  link:match("%(<?(.-)>?%)") or "" | 
|    944 		url = encode_alt(url) | 
   805       url = encode_alt(url) | 
|    945 		if title then | 
   806       if title then | 
|    946 			return add_escape("<a href=\"" .. url .. "\" title=\"" .. title .. "\">") .. text .. "</a>" | 
   807          return add_escape("<a href=\"" .. url .. "\" title=\"" .. title .. "\">") .. text .. "</a>" | 
|    947 		else | 
   808       else | 
|    948 			return add_escape("<a href=\"" .. url .. "\">") .. text .. add_escape("</a>") | 
   809          return add_escape("<a href=\"" .. url .. "\">") .. text .. add_escape("</a>") | 
|    949 		end | 
   810       end | 
|    950 	end | 
   811    end | 
|    951 	 | 
   812  | 
|    952 	text = text:gsub("(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) | 
   813    text = text:gsub("(%b[])[ \t]*\n?[ \t]*(%b[])", reference_link) | 
|    953 	text = text:gsub("(%b[])(%b())", inline_link) | 
   814    text = text:gsub("(%b[])(%b())", inline_link) | 
|    954 	return text | 
   815    return text | 
|    955 end | 
   816 end | 
|    956  | 
   817  | 
|    957 -- Handle auto links, i.e. <http://www.google.com/>. | 
   818 -- Handle auto links, i.e. <http://www.google.com/>. | 
|    958 function auto_links(text) | 
   819 local function auto_links(text) | 
|    959 	local function link(s) | 
   820    local function link(s) | 
|    960 		return add_escape("<a href=\"" .. s .. "\">") .. s .. "</a>" | 
   821       return add_escape("<a href=\"" .. s .. "\">") .. s .. "</a>" | 
|    961 	end | 
   822    end | 
|    962 	-- Encode chars as a mix of dec and hex entitites to (perhaps) fool | 
   823    -- Encode chars as a mix of dec and hex entitites to (perhaps) fool | 
|    963 	-- spambots. | 
   824    -- spambots. | 
|    964 	local function encode_email_address(s) | 
   825    local function encode_email_address(s) | 
|    965 		-- Use a deterministic encoding to make unit testing possible. | 
   826       -- Use a deterministic encoding to make unit testing possible. | 
|    966 		-- Code 45% hex, 45% dec, 10% plain. | 
   827       -- Code 45% hex, 45% dec, 10% plain. | 
|    967 		local hex = {code = function(c) return "&#x" .. string.format("%x", c:byte()) .. ";" end, count = 1, rate = 0.45} | 
   828       local hex = {code = function(c) return "&#x" .. string.format("%x", c:byte()) .. ";" end, count = 1, rate = 0.45} | 
|    968 		local dec = {code = function(c) return "&#" .. c:byte() .. ";" end, count = 0, rate = 0.45} | 
   829       local dec = {code = function(c) return "&#" .. c:byte() .. ";" end, count = 0, rate = 0.45} | 
|    969 		local plain = {code = function(c) return c end, count = 0, rate = 0.1} | 
   830       local plain = {code = function(c) return c end, count = 0, rate = 0.1} | 
|    970 		local codes = {hex, dec, plain} | 
   831       local codes = {hex, dec, plain} | 
|    971 		local function swap(t,k1,k2) local temp = t[k2] t[k2] = t[k1] t[k1] = temp end | 
   832       local function swap(t,k1,k2) local temp = t[k2] t[k2] = t[k1] t[k1] = temp end | 
|    972 		 | 
   833  | 
|    973 		local out = "" | 
   834       local out = "" | 
|    974 		for i = 1,s:len() do | 
   835       for i = 1,s:len() do | 
|    975 			for _,code in ipairs(codes) do code.count = code.count + code.rate end | 
   836          for _,code in ipairs(codes) do code.count = code.count + code.rate end | 
|    976 			if codes[1].count < codes[2].count then swap(codes,1,2) end | 
   837          if codes[1].count < codes[2].count then swap(codes,1,2) end | 
|    977 			if codes[2].count < codes[3].count then swap(codes,2,3) end | 
   838          if codes[2].count < codes[3].count then swap(codes,2,3) end | 
|    978 			if codes[1].count < codes[2].count then swap(codes,1,2) end | 
   839          if codes[1].count < codes[2].count then swap(codes,1,2) end | 
|    979 			 | 
   840  | 
|    980 			local code = codes[1] | 
   841          local code = codes[1] | 
|    981 			local c = s:sub(i,i) | 
   842          local c = s:sub(i,i) | 
|    982 			-- Force encoding of "@" to make email address more invisible. | 
   843          -- Force encoding of "@" to make email address more invisible. | 
|    983 			if c == "@" and code == plain then code = codes[2] end | 
   844          if c == "@" and code == plain then code = codes[2] end | 
|    984 			out = out .. code.code(c) | 
   845          out = out .. code.code(c) | 
|    985 			code.count = code.count - 1 | 
   846          code.count = code.count - 1 | 
|    986 		end	 | 
   847       end | 
|    987 		return out | 
   848       return out | 
|    988 	end | 
   849    end | 
|    989 	local function mail(s) | 
   850    local function mail(s) | 
|    990 		s = unescape_special_chars(s) | 
   851       s = unescape_special_chars(s) | 
|    991 		local address = encode_email_address("mailto:" .. s) | 
   852       local address = encode_email_address("mailto:" .. s) | 
|    992 		local text = encode_email_address(s) | 
   853       local text = encode_email_address(s) | 
|    993 		return add_escape("<a href=\"" .. address .. "\">") .. text .. "</a>" | 
   854       return add_escape("<a href=\"" .. address .. "\">") .. text .. "</a>" | 
|    994 	end | 
   855    end | 
|    995 	-- links | 
   856    -- links | 
|    996 	text = text:gsub("<(https?:[^'\">%s]+)>", link) | 
   857    text = text:gsub("<(https?:[^'\">%s]+)>", link) | 
|    997 	text = text:gsub("<(ftp:[^'\">%s]+)>", link) | 
   858    text = text:gsub("<(ftp:[^'\">%s]+)>", link) | 
|    998 	 | 
   859  | 
|    999 	-- mail | 
   860    -- mail | 
|   1000 	text = text:gsub("<mailto:([^'\">%s]+)>", mail) | 
   861    text = text:gsub("<mailto:([^'\">%s]+)>", mail) | 
|   1001 	text = text:gsub("<([-.%w]+%@[-.%w]+)>", mail) | 
   862    text = text:gsub("<([-.%w]+%@[-.%w]+)>", mail) | 
|   1002 	return text | 
   863    return text | 
|   1003 end | 
   864 end | 
|   1004  | 
   865  | 
|   1005 -- Encode free standing amps (&) and angles (<)... note that this does not | 
   866 -- Encode free standing amps (&) and angles (<)... note that this does not | 
|   1006 -- encode free >. | 
   867 -- encode free >. | 
|   1007 function amps_and_angles(s) | 
   868 local function amps_and_angles(s) | 
|   1008 	-- encode amps not part of &..; expression | 
   869    -- encode amps not part of &..; expression | 
|   1009 	local pos = 1 | 
   870    local pos = 1 | 
|   1010 	while true do | 
   871    while true do | 
|   1011 		local amp = s:find("&", pos) | 
   872       local amp = s:find("&", pos) | 
|   1012 		if not amp then break end | 
   873       if not amp then break end | 
|   1013 		local semi = s:find(";", amp+1) | 
   874       local semi = s:find(";", amp+1) | 
|   1014 		local stop = s:find("[ \t\n&]", amp+1) | 
   875       local stop = s:find("[ \t\n&]", amp+1) | 
|   1015 		if not semi or (stop and stop < semi) or (semi - amp) > 15 then | 
   876       if not semi or (stop and stop < semi) or (semi - amp) > 15 then | 
|   1016 			s = s:sub(1,amp-1) .. "&" .. s:sub(amp+1) | 
   877          s = s:sub(1,amp-1) .. "&" .. s:sub(amp+1) | 
|   1017 			pos = amp+1 | 
   878          pos = amp+1 | 
|   1018 		else | 
   879       else | 
|   1019 			pos = amp+1 | 
   880          pos = amp+1 | 
|   1020 		end | 
   881       end | 
|   1021 	end | 
   882    end | 
|   1022 	 | 
   883  | 
|   1023 	-- encode naked <'s | 
   884    -- encode naked <'s | 
|   1024 	s = s:gsub("<([^a-zA-Z/?$!])", "<%1") | 
   885    s = s:gsub("<([^a-zA-Z/?$!])", "<%1") | 
|   1025 	s = s:gsub("<$", "<") | 
   886    s = s:gsub("<$", "<") | 
|   1026 	 | 
   887  | 
|   1027 	-- what about >, nothing done in the original markdown source to handle them | 
   888    -- what about >, nothing done in the original markdown source to handle them | 
|   1028 	return s | 
   889    return s | 
|   1029 end | 
   890 end | 
|   1030  | 
   891  | 
|   1031 -- Handles emphasis markers (* and _) in the text. | 
   892 -- Handles emphasis markers (* and _) in the text. | 
|   1032 function emphasis(text) | 
   893 local function emphasis(text) | 
|   1033 	for _, s in ipairs {"%*%*", "%_%_"} do | 
   894    for _, s in ipairs {"%*%*", "%_%_"} do | 
|   1034 		text = text:gsub(s .. "([^%s][%*%_]?)" .. s, "<strong>%1</strong>") | 
   895       text = text:gsub(s .. "([^%s][%*%_]?)" .. s, "<strong>%1</strong>") | 
|   1035 		text = text:gsub(s .. "([^%s][^<>]-[^%s][%*%_]?)" .. s, "<strong>%1</strong>") | 
   896       text = text:gsub(s .. "([^%s][^<>]-[^%s][%*%_]?)" .. s, "<strong>%1</strong>") | 
|   1036 	end | 
   897    end | 
|   1037 	for _, s in ipairs {"%*", "%_"} do | 
   898    for _, s in ipairs {"%*", "%_"} do | 
|   1038 		text = text:gsub(s .. "([^%s_])" .. s, "<em>%1</em>") | 
   899       text = text:gsub(s .. "([^%s_])" .. s, "<em>%1</em>") | 
|   1039 		text = text:gsub(s .. "(<strong>[^%s_]</strong>)" .. s, "<em>%1</em>") | 
   900       text = text:gsub(s .. "(<strong>[^%s_]</strong>)" .. s, "<em>%1</em>") | 
|   1040 		text = text:gsub(s .. "([^%s_][^<>_]-[^%s_])" .. s, "<em>%1</em>") | 
   901       text = text:gsub(s .. "([^%s_][^<>_]-[^%s_])" .. s, "<em>%1</em>") | 
|   1041 		text = text:gsub(s .. "([^<>_]-<strong>[^<>_]-</strong>[^<>_]-)" .. s, "<em>%1</em>") | 
   902       text = text:gsub(s .. "([^<>_]-<strong>[^<>_]-</strong>[^<>_]-)" .. s, "<em>%1</em>") | 
|   1042 	end | 
   903    end | 
|   1043 	return text | 
   904    return text | 
|   1044 end | 
   905 end | 
|   1045  | 
   906  | 
|   1046 -- Handles line break markers in the text. | 
   907 -- Handles line break markers in the text. | 
|   1047 function line_breaks(text) | 
   908 local function line_breaks(text) | 
|   1048 	return text:gsub("  +\n", " <br/>\n") | 
   909    return text:gsub("  +\n", " <br/>\n") | 
|   1049 end | 
   910 end | 
|   1050  | 
   911  | 
|   1051 -- Perform all span level transforms. | 
   912 -- Perform all span level transforms. | 
|   1052 function span_transform(text) | 
   913 function span_transform(text) | 
|   1053 	text = code_spans(text) | 
   914    text = code_spans(text) | 
|   1054 	text = escape_special_chars(text) | 
   915    text = escape_special_chars(text) | 
|   1055 	text = images(text) | 
   916    text = images(text) | 
|   1056 	text = anchors(text) | 
   917    text = anchors(text) | 
|   1057 	text = auto_links(text) | 
   918    text = auto_links(text) | 
|   1058 	text = amps_and_angles(text) | 
   919    text = amps_and_angles(text) | 
|   1059 	text = emphasis(text) | 
   920    text = emphasis(text) | 
|   1060 	text = line_breaks(text) | 
   921    text = line_breaks(text) | 
|   1061 	return text | 
   922    return text | 
|   1062 end | 
   923 end | 
|   1063  | 
   924  | 
|   1064 ---------------------------------------------------------------------- | 
   925 ---------------------------------------------------------------------- | 
|   1065 -- Markdown | 
   926 -- Markdown | 
|   1066 ---------------------------------------------------------------------- | 
   927 ---------------------------------------------------------------------- | 
|   1067  | 
   928  | 
|   1068 -- Cleanup the text by normalizing some possible variations to make further | 
   929 -- Cleanup the text by normalizing some possible variations to make further | 
|   1069 -- processing easier. | 
   930 -- processing easier. | 
|   1070 function cleanup(text) | 
   931 local function cleanup(text) | 
|   1071 	-- Standardize line endings | 
   932    -- Standardize line endings | 
|   1072 	text = text:gsub("\r\n", "\n")  -- DOS to UNIX | 
   933    text = text:gsub("\r\n", "\n")  -- DOS to UNIX | 
|   1073 	text = text:gsub("\r", "\n")    -- Mac to UNIX | 
   934    text = text:gsub("\r", "\n")    -- Mac to UNIX | 
|   1074 	 | 
   935  | 
|   1075 	-- Convert all tabs to spaces | 
   936    -- Convert all tabs to spaces | 
|   1076 	text = detab(text) | 
   937    text = detab(text) | 
|   1077 	 | 
   938  | 
|   1078 	-- Strip lines with only spaces and tabs | 
   939    -- Strip lines with only spaces and tabs | 
|   1079 	while true do | 
   940    while true do | 
|   1080 		local subs | 
   941       local subs | 
|   1081 		text, subs = text:gsub("\n[ \t]+\n", "\n\n") | 
   942       text, subs = text:gsub("\n[ \t]+\n", "\n\n") | 
|   1082 		if subs == 0 then break end | 
   943       if subs == 0 then break end | 
|   1083 	end | 
   944    end | 
|   1084 	 | 
   945  | 
|   1085 	return "\n" .. text .. "\n" | 
   946    return "\n" .. text .. "\n" | 
|   1086 end | 
   947 end | 
|   1087  | 
   948  | 
|   1088 -- Strips link definitions from the text and stores the data in a lookup table. | 
   949 -- Strips link definitions from the text and stores the data in a lookup table. | 
|   1089 function strip_link_definitions(text) | 
   950 local function strip_link_definitions(text) | 
|   1090 	local linkdb = {} | 
   951    local linkdb = {} | 
|   1091 	 | 
   952  | 
|   1092 	local function link_def(id, url, title) | 
   953    local function link_def(id, url, title) | 
|   1093 		id = id:match("%[(.+)%]"):lower() | 
   954       id = id:match("%[(.+)%]"):lower() | 
|   1094 		linkdb[id] = linkdb[id] or {} | 
   955       linkdb[id] = linkdb[id] or {} | 
|   1095 		linkdb[id].url = url or linkdb[id].url | 
   956       linkdb[id].url = url or linkdb[id].url | 
|   1096 		linkdb[id].title = title or linkdb[id].title | 
   957       linkdb[id].title = title or linkdb[id].title | 
|   1097 		return "" | 
   958       return "" | 
|   1098 	end | 
   959    end | 
|   1099  | 
   960  | 
|   1100 	local def_no_title = "\n ? ? ?(%b[]):[ \t]*\n?[ \t]*<?([^%s>]+)>?[ \t]*" | 
   961    local def_no_title = "\n ? ? ?(%b[]):[ \t]*\n?[ \t]*<?([^%s>]+)>?[ \t]*" | 
|   1101 	local def_title1 = def_no_title .. "[ \t]+\n?[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" | 
   962    local def_title1 = def_no_title .. "[ \t]+\n?[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" | 
|   1102 	local def_title2 = def_no_title .. "[ \t]*\n[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" | 
   963    local def_title2 = def_no_title .. "[ \t]*\n[ \t]*[\"'(]([^\n]+)[\"')][ \t]*" | 
|   1103 	local def_title3 = def_no_title .. "[ \t]*\n?[ \t]+[\"'(]([^\n]+)[\"')][ \t]*" | 
   964    local def_title3 = def_no_title .. "[ \t]*\n?[ \t]+[\"'(]([^\n]+)[\"')][ \t]*" | 
|   1104 	 | 
   965  | 
|   1105 	text = text:gsub(def_title1, link_def) | 
   966    text = text:gsub(def_title1, link_def) | 
|   1106 	text = text:gsub(def_title2, link_def) | 
   967    text = text:gsub(def_title2, link_def) | 
|   1107 	text = text:gsub(def_title3, link_def) | 
   968    text = text:gsub(def_title3, link_def) | 
|   1108 	text = text:gsub(def_no_title, link_def) | 
   969    text = text:gsub(def_no_title, link_def) | 
|   1109 	return text, linkdb | 
   970    return text, linkdb | 
|   1110 end | 
   971 end | 
|   1111  | 
        | 
|   1112 link_database = {} | 
        | 
|   1113  | 
   972  | 
|   1114 -- Main markdown processing function | 
   973 -- Main markdown processing function | 
|   1115 function markdown(text) | 
   974 local function markdown(text) | 
|   1116 	init_hash(text) | 
   975    init_hash(text) | 
|   1117 	init_escape_table() | 
   976    init_escape_table() | 
|   1118 	 | 
   977  | 
|   1119 	text = cleanup(text) | 
   978    text = cleanup(text) | 
|   1120 	text = protect(text) | 
   979    text = protect(text) | 
|   1121 	text, link_database = strip_link_definitions(text) | 
   980    text, link_database = strip_link_definitions(text) | 
|   1122 	text = block_transform(text) | 
   981    text = block_transform(text) | 
|   1123 	text = unescape_special_chars(text) | 
   982    text = unescape_special_chars(text) | 
|   1124 	return text | 
   983    return text | 
|   1125 end | 
   984 end | 
|   1126  | 
   985  | 
|   1127 ---------------------------------------------------------------------- | 
   986 ---------------------------------------------------------------------- | 
|   1128 -- End of module | 
   987 -- End of module | 
|   1129 ---------------------------------------------------------------------- | 
   988 ---------------------------------------------------------------------- | 
|   1130  | 
   989  | 
|   1131 setfenv(1, _G) | 
   990 -- For compatibility, set markdown function as a global | 
|   1132 M.lock(M) | 
   991 _G.markdown = markdown | 
|   1133  | 
        | 
|   1134 -- Expose markdown function to the world | 
        | 
|   1135 markdown = M.markdown | 
        | 
|   1136  | 
   992  | 
|   1137 -- Class for parsing command-line options | 
   993 -- Class for parsing command-line options | 
|   1138 local OptionParser = {} | 
   994 local OptionParser = {} | 
|   1139 OptionParser.__index = OptionParser | 
   995 OptionParser.__index = OptionParser | 
|   1140  | 
   996  | 
|   1141 -- Creates a new option parser | 
   997 -- Creates a new option parser | 
|   1142 function OptionParser:new() | 
   998 function OptionParser:new() | 
|   1143 	local o = {short = {}, long = {}} | 
   999    local o = {short = {}, long = {}} | 
|   1144 	setmetatable(o, self) | 
  1000    setmetatable(o, self) | 
|   1145 	return o | 
  1001    return o | 
|   1146 end | 
  1002 end | 
|   1147  | 
  1003  | 
|   1148 -- Calls f() whenever a flag with specified short and long name is encountered | 
  1004 -- Calls f() whenever a flag with specified short and long name is encountered | 
|   1149 function OptionParser:flag(short, long, f) | 
  1005 function OptionParser:flag(short, long, f) | 
|   1150 	local info = {type = "flag", f = f} | 
  1006    local info = {type = "flag", f = f} | 
|   1151 	if short then self.short[short] = info end | 
  1007    if short then self.short[short] = info end | 
|   1152 	if long then self.long[long] = info end | 
  1008    if long then self.long[long] = info end | 
|   1153 end | 
  1009 end | 
|   1154  | 
  1010  | 
|   1155 -- Calls f(param) whenever a parameter flag with specified short and long name is encountered | 
  1011 -- Calls f(param) whenever a parameter flag with specified short and long name is encountered | 
|   1156 function OptionParser:param(short, long, f) | 
  1012 function OptionParser:param(short, long, f) | 
|   1157 	local info = {type = "param", f = f} | 
  1013    local info = {type = "param", f = f} | 
|   1158 	if short then self.short[short] = info end | 
  1014    if short then self.short[short] = info end | 
|   1159 	if long then self.long[long] = info end | 
  1015    if long then self.long[long] = info end | 
|   1160 end | 
  1016 end | 
|   1161  | 
  1017  | 
|   1162 -- Calls f(v) for each non-flag argument | 
  1018 -- Calls f(v) for each non-flag argument | 
|   1163 function OptionParser:arg(f) | 
  1019 function OptionParser:arg(f) | 
|   1164 	self.arg = f | 
  1020    self.arg = f | 
|   1165 end | 
  1021 end | 
|   1166  | 
  1022  | 
|   1167 -- Runs the option parser for the specified set of arguments. Returns true if all arguments | 
  1023 -- Runs the option parser for the specified set of arguments. Returns true if all arguments | 
|   1168 -- where successfully parsed and false otherwise. | 
  1024 -- where successfully parsed and false otherwise. | 
|   1169 function OptionParser:run(args) | 
  1025 function OptionParser:run(args) | 
|   1170 	local pos = 1 | 
  1026    local pos = 1 | 
|   1171 	while pos <= #args do | 
  1027    while pos <= #args do | 
|   1172 		local arg = args[pos] | 
  1028       local arg = args[pos] | 
|   1173 		if arg == "--" then | 
  1029       if arg == "--" then | 
|   1174 			for i=pos+1,#args do | 
  1030          for i=pos+1,#args do | 
|   1175 				if self.arg then self.arg(args[i]) end | 
  1031             if self.arg then self.arg(args[i]) end | 
|   1176 				return true | 
  1032             return true | 
|   1177 			end | 
  1033          end | 
|   1178 		end | 
  1034       end | 
|   1179 		if arg:match("^%-%-") then | 
  1035       if arg:match("^%-%-") then | 
|   1180 			local info = self.long[arg:sub(3)] | 
  1036          local info = self.long[arg:sub(3)] | 
|   1181 			if not info then print("Unknown flag: " .. arg) return false end | 
  1037          if not info then print("Unknown flag: " .. arg) return false end | 
|   1182 			if info.type == "flag" then | 
  1038          if info.type == "flag" then | 
|   1183 				info.f() | 
  1039             info.f() | 
|   1184 				pos = pos + 1 | 
  1040             pos = pos + 1 | 
|   1185 			else | 
  1041          else | 
|   1186 				param = args[pos+1] | 
  1042             local param = args[pos+1] | 
|   1187 				if not param then print("No parameter for flag: " .. arg) return false end | 
  1043             if not param then print("No parameter for flag: " .. arg) return false end | 
|   1188 				info.f(param) | 
  1044             info.f(param) | 
|   1189 				pos = pos+2 | 
  1045             pos = pos+2 | 
|   1190 			end | 
  1046          end | 
|   1191 		elseif arg:match("^%-") then | 
  1047       elseif arg:match("^%-") then | 
|   1192 			for i=2,arg:len() do | 
  1048          for i=2,arg:len() do | 
|   1193 				local c = arg:sub(i,i) | 
  1049             local c = arg:sub(i,i) | 
|   1194 				local info = self.short[c] | 
  1050             local info = self.short[c] | 
|   1195 				if not info then print("Unknown flag: -" .. c) return false end | 
  1051             if not info then print("Unknown flag: -" .. c) return false end | 
|   1196 				if info.type == "flag" then | 
  1052             if info.type == "flag" then | 
|   1197 					info.f() | 
  1053                info.f() | 
|   1198 				else | 
  1054             else | 
|   1199 					if i == arg:len() then | 
  1055                if i == arg:len() then | 
|   1200 						param = args[pos+1] | 
  1056                   local param = args[pos+1] | 
|   1201 						if not param then print("No parameter for flag: -" .. c) return false end | 
  1057                   if not param then print("No parameter for flag: -" .. c) return false end | 
|   1202 						info.f(param) | 
  1058                   info.f(param) | 
|   1203 						pos = pos + 1 | 
  1059                   pos = pos + 1 | 
|   1204 					else | 
  1060                else | 
|   1205 						param = arg:sub(i+1) | 
  1061                   local param = arg:sub(i+1) | 
|   1206 						info.f(param) | 
  1062                   info.f(param) | 
|   1207 					end | 
  1063                end | 
|   1208 					break | 
  1064                break | 
|   1209 				end | 
  1065             end | 
|   1210 			end | 
  1066          end | 
|   1211 			pos = pos + 1 | 
  1067          pos = pos + 1 | 
|   1212 		else | 
  1068       else | 
|   1213 			if self.arg then self.arg(arg) end | 
  1069          if self.arg then self.arg(arg) end | 
|   1214 			pos = pos + 1 | 
  1070          pos = pos + 1 | 
|   1215 		end | 
  1071       end | 
|   1216 	end | 
  1072    end | 
|   1217 	return true | 
  1073    return true | 
|         | 
  1074 end | 
|         | 
  1075  | 
|         | 
  1076 local function read_file(path, descr) | 
|         | 
  1077    local file = io.open(path) or error("Could not open " .. descr .. " file: " .. path) | 
|         | 
  1078    local contents = file:read("*a") or error("Could not read " .. descr .. " from " .. path) | 
|         | 
  1079    file:close() | 
|         | 
  1080    return contents | 
|   1218 end | 
  1081 end | 
|   1219  | 
  1082  | 
|   1220 -- Handles the case when markdown is run from the command line | 
  1083 -- Handles the case when markdown is run from the command line | 
|   1221 local function run_command_line(arg) | 
  1084 local function run_command_line(arg) | 
|   1222 	-- Generate output for input s given options | 
  1085    -- Generate output for input s given options | 
|   1223 	local function run(s, options) | 
  1086    local function run(s, options) | 
|   1224 		s = markdown(s) | 
  1087       s = markdown(s) | 
|   1225 		if not options.wrap_header then return s end | 
  1088       if not options.wrap_header then return s end | 
|   1226 		local header = "" | 
  1089       local header | 
|   1227 		if options.header then | 
  1090       if options.header then | 
|   1228 			local f = io.open(options.header) or error("Could not open file: " .. options.header)  | 
  1091          header = read_file(options.header, "header") | 
|   1229 			header = f:read("*a") | 
  1092       else | 
|   1230 			f:close() | 
  1093          header = [[ | 
|   1231 		else | 
        | 
|   1232 			header = [[ | 
        | 
|   1233 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | 
  1094 <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> | 
|   1234 <html> | 
  1095 <html> | 
|   1235 <head> | 
  1096 <head> | 
|   1236 	<meta http-equiv="content-type" content="text/html; charset=CHARSET" /> | 
  1097     <meta http-equiv="content-type" content="text/html; charset=CHARSET" /> | 
|   1237 	<title>TITLE</title> | 
  1098     <title>TITLE</title> | 
|   1238 	<link rel="stylesheet" type="text/css" href="STYLESHEET" /> | 
  1099     <link rel="stylesheet" type="text/css" href="STYLESHEET" /> | 
|   1239 </head> | 
  1100 </head> | 
|   1240 <body> | 
  1101 <body> | 
|   1241 ]] | 
  1102 ]] | 
|   1242 			local title = options.title or s:match("<h1>(.-)</h1>") or s:match("<h2>(.-)</h2>") or  | 
  1103          local title = options.title or s:match("<h1>(.-)</h1>") or s:match("<h2>(.-)</h2>") or | 
|   1243 				s:match("<h3>(.-)</h3>") or "Untitled" | 
  1104             s:match("<h3>(.-)</h3>") or "Untitled" | 
|   1244 			header = header:gsub("TITLE", title) | 
  1105          header = header:gsub("TITLE", title) | 
|   1245 			if options.inline_style then | 
  1106          if options.inline_style then | 
|   1246 				local style = "" | 
  1107             local style = read_file(options.stylesheet, "style sheet") | 
|   1247 				local f = io.open(options.stylesheet) | 
  1108             header = header:gsub('<link rel="stylesheet" type="text/css" href="STYLESHEET" />', | 
|   1248 				if f then  | 
  1109                "<style type=\"text/css\"><!--\n" .. style .. "\n--></style>") | 
|   1249 					style = f:read("*a") f:close()  | 
  1110          else | 
|   1250 				else | 
  1111             header = header:gsub("STYLESHEET", options.stylesheet) | 
|   1251 					error("Could not include style sheet " .. options.stylesheet .. ": File not found") | 
  1112          end | 
|   1252 				end | 
  1113          header = header:gsub("CHARSET", options.charset) | 
|   1253 				header = header:gsub('<link rel="stylesheet" type="text/css" href="STYLESHEET" />', | 
  1114       end | 
|   1254 					"<style type=\"text/css\"><!--\n" .. style .. "\n--></style>") | 
  1115       local footer = "</body></html>" | 
|   1255 			else | 
  1116       if options.footer then | 
|   1256 				header = header:gsub("STYLESHEET", options.stylesheet) | 
  1117          footer = read_file(options.footer, "footer") | 
|   1257 			end | 
  1118       end | 
|   1258 			header = header:gsub("CHARSET", options.charset) | 
  1119       return header .. s .. footer | 
|   1259 		end | 
  1120    end | 
|   1260 		local footer = "</body></html>" | 
  1121  | 
|   1261 		if options.footer then | 
  1122    -- Generate output path name from input path name given options. | 
|   1262 			local f = io.open(options.footer) or error("Could not open file: " .. options.footer) | 
  1123    local function outpath(path, options) | 
|   1263 			footer = f:read("*a") | 
  1124       if options.append then return path .. ".html" end | 
|   1264 			f:close() | 
  1125       local m = path:match("^(.+%.html)[^/\\]+$") if m then return m end | 
|   1265 		end | 
  1126       m = path:match("^(.+%.)[^/\\]*$") if m and path ~= m .. "html" then return m .. "html" end | 
|   1266 		return header .. s .. footer | 
  1127       return path .. ".html" | 
|   1267 	end | 
  1128    end | 
|   1268 	 | 
  1129  | 
|   1269 	-- Generate output path name from input path name given options.	 | 
  1130    -- Default commandline options | 
|   1270 	local function outpath(path, options) | 
  1131    local options = { | 
|   1271 		if options.append then return path .. ".html" end | 
  1132       wrap_header = true, | 
|   1272 		local m = path:match("^(.+%.html)[^/\\]+$") if m then return m end | 
  1133       header = nil, | 
|   1273 		m = path:match("^(.+%.)[^/\\]*$") if m and path ~= m .. "html" then return m .. "html" end | 
  1134       footer = nil, | 
|   1274 		return path .. ".html" | 
  1135       charset = "utf-8", | 
|   1275 	end | 
  1136       title = nil, | 
|   1276 	 | 
  1137       stylesheet = "default.css", | 
|   1277 	-- Default commandline options | 
  1138       inline_style = false | 
|   1278 	local options = { | 
  1139    } | 
|   1279 		wrap_header = true, | 
  1140    local help = [[ | 
|   1280 		header = nil, | 
        | 
|   1281 		footer = nil, | 
        | 
|   1282 		charset = "utf-8", | 
        | 
|   1283 		title = nil, | 
        | 
|   1284 		stylesheet = "default.css", | 
        | 
|   1285 		inline_style = false | 
        | 
|   1286 	} | 
        | 
|   1287 	local help = [[ | 
        | 
|   1288 Usage: markdown.lua [OPTION] [FILE] | 
  1141 Usage: markdown.lua [OPTION] [FILE] | 
|   1289 Runs the markdown text markup to HTML converter on each file specified on the | 
  1142 Runs the markdown text markup to HTML converter on each file specified on the | 
|   1290 command line. If no files are specified, runs on standard input. | 
  1143 command line. If no files are specified, runs on standard input. | 
|   1291  | 
  1144  | 
|   1292 No header: | 
  1145 No header: |