1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204
function toHTML(code) { code = removeTags(code) code = convertTables(code) code = convertLists(code) code = removeBlankLines(code) code = convertHeaders(code) code = convertParagraphs(code) code = convertNewLines(code) code = convertLinks(code) code = convertBolds(code) code = convertItalics(code) return code } /** * Removes any html code blocks by encoding '<' and '>' */ function removeTags(code) { code = code.replace(/>/g,">") return code.replace(/</g,"<") } /** * Removes blank lines such that there is at most one blank line between texts */ function removeBlankLines(code) { return code.replace(/\n\n+\n/gm,"\n\n") } /** * Takes links in the format of "link text":[link (with spaces)] and converts * it to a valid href link */ function convertLinks(code) { return code.replace(/"([^"]*)":\[([^\]]*)\]/gm,function(match,text,url) { return "<a href=\""+url+"\">"+text+"</a>" }) } /** * Takes headers in the form of "h#. Header Text" and surrounds them with * <h#> tags */ function convertHeaders(code) { return code.replace(/^[hH](\d). *(.*) */gm,function(match,number,text) { return "<h"+number+">"+text+"</h"+number+">" }) } /** * Replaces any new lines that do not signify a new paragraph with <br> tags */ function convertNewLines(code) { /*return code.replace(/(?!\n)/gm,function(match,text) { return text+"<br>" })*/ return code.replace(/(.| )$\n(?!(\n|<))/gm,function(match,text) { return text+"<br>\n" }) } /** * Takes any block of text not already converted into HTML and surrounds it with * <p> tags. A block of text is one which has a blank line either preceding or * proceding it. */ function convertParagraphs(code) { return code.replace(/^\n?(([^<\n]|\n(?!\n)| )*)/gm,function(match,text) { if(text) return "\n<p>"+text+"</p>" return "" }) } /** * Converts text surrounded by two consecutive '*' into bolded text with * <strong> tags */ function convertBolds(code) { return code.replace(/\*\*(.+)\*\*/gm,function(match,text) { return "<strong>"+text+"</strong>" }) } /** * Converts text surrounded by a single '*' into italics text with <em> tags */ function convertItalics(code) { return code.replace(/\*(.+)\*/gm,function(match,text) { return "<em>"+text+"</em>" }) } /** * Takes blocks of text which look like the below and form them into a valid * HTML table. Pipes ('|') signify the start of a new cell * Forward-slashes ('/') signify the end of a row * * | cell 1 | cell 2 / * | cell 3 | cell 4 / * * <table> * <tr> * <td>cell 1</td><td>cell 2</td> * </tr> * <tr> * <td>cell 3</td><td>cell 4</td> * </tr> * </table> */ function convertTables(code) { return code.replace(/\|(.|\n(?!\n))*\//g,function(match) { match = match.replace(/^\| *(?=.)/gm,"<tr><td>") match = match.replace(/ *\| *(?=.)/gm,"</td><td>") match = match.replace(/ *\/$/gm,"</td></tr>") return "<table>"+match+"</table>" }) } /** * Let a list Item be defined by " *[-*+]|([a-zA-Z0-9](.|\))) *" * Let the number of spaces before a list Item be the Item Rank * Let the subness of a list be its Order * * Let there be a relational list between previous Ranks and their Order called R/O-Prev * * If R/O-Prev is empty then Order = 1 * If the Rank is in R/O-Prev then Order = R/O-Prev[Rank] * If the Rank is greater than previous then the Order is increased by 1 * If the Rank is lesser than previous then Order = Order associated with the closest but less Rank * Association is set in R/O-Prev and all greater Rank associations are nullified */ function convertLists(code) { code = code.replace(/^ *[-*+](.|\n(?!\n))*/gm,function(match) { var html = "" var ranks = [] //order = index, rank = ranks[index] var lists = [] //order = index, type = lists[index] = "</ul>" or "</ol>" var getRank = function(arrRanks, curRank) { for(var i=0;i<arrRanks.length;i++) if(arrRanks[i]==curRank) return i else if(arrRanks[i]>curRank) return i-1>=0?i-1:0 return arrRanks.length } //var bullet = /^( *)[-*+]((.|\n *[\w\d])*)/gm /** * ^( *) #number of spaces * ([-*+]|[a-zA-Z0-9][).]) #orderd vs unordered * ((.|\n *[\w\d]{2,})*) #to end of line or next line if not * #beginning of new item */ var bullet = /^( *)([-*+]|[a-zA-Z0-9][).])((.|\n *[\w\d]{2,})*)/gm var prevOrder = -1, prevRank = -1, order, rank while((item = bullet.exec(match))!=null) { rank = item[1].length //figure out the order if(ranks.length==0) order = 0 else { if(rank>prevRank) { order = ranks.length ranks[ranks.length] = rank } else { order = getRank(ranks,rank) } } ranks[order] = rank //ensure rank is in there //reinit spArr if(order<prevOrder) { var nRanks = [] for(var i=0;i<=order;i++) nRanks[i] = ranks[i] ranks = nRanks } /** * Apply HTML. Each item should be given an open */ if(order>prevOrder) { if(item[2].substr(0,1)=='-') { html += "\n<ul>" lists[order] = "\n</ul>" } else { html += "<ol>" lists[order] = "</ol>" } html += "<li>"+item[3] } else if(order<prevOrder) { for(var i=prevOrder;i>order;i--) html+="</li>"+lists[i] html += "</li><li>"+item[3] } else html += "</li><li>"+item[3] //order and rank are now old prevRank = rank prevOrder = order } html += "</li></ul>" return html }) return code }
Refactorings
No refactoring yet !
Zodoz
July 3, 2008, July 03, 2008 01:47, permalink
Oh yea, and here is my test page for this code.
Editor
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"> <html lang="en"> <head> <title>Me Edit</title> <script type="text/javascript" src="js/meedit.js"></script> <script type="text/javascript"> function $(id) { return document.getElementById(id) } function convert() { var val = $('inputPane').value var html = toHTML(val) $('output').innerHTML = html $('code').innerHTML = toHTMLCode(html) return } function onload() { convert() } function toHTMLCode(html) { html = removeTags(html) html = convertParagraphs(html) html = html.replace(/(.| )$\n(?!\n)/gm,function(match,text) { return text+"<br/>"//+"<br>" }) return html } </script> </head> <body> <div id="input"> <textarea onkeyup="convert()" id="inputPane" cols="80" rows="10"></textarea> </div> <table style="max-width:600px"> <tr> <td style="max-width:300px"><span id="output"/></td> <td style="max-width:300px"><span overflow="auto" id="code"/></td> </tr> </table> </body> </html>
Hey all, I've created a Markdown-like javascript "compiler" which will take basic text in a textbox and can generate equivalent HTML in real time. My purpose is to make this code as easy to read, well documented, and easily maintainable as possible. While the syntax is my own (pulled from Markdown and others like it) its purpose is fueled by not finding any other editors that could do exactly what I wanted (or that were easy to change). The end result is going to end up on our Mechanical Engineering Department's website for the professors to edit their site and class pages.
This is supposed to be a simple enough syntax that anyone can pick it up quickly, but have enough features to do most everything they could want to do. With that in mind, I am still working on some other features such as image placement and other possible improvements (suggestions are always welcome!). Also, the code that it generates is supposed to be basic HTML; all the style will come from CSS.