2bf2366ae73ca0d6974cad4575a16472

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.

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,"&gt;")
	return code.replace(/</g,"&lt;")
}

/**
 * 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 !

2bf2366ae73ca0d6974cad4575a16472

Zodoz

July 3, 2008, July 03, 2008 01:47, permalink

No rating. Login to rate!

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>

Your refactoring





Format Copy from initial code

or Cancel