├── wmd-buttons.png ├── wmd-buttons.psd ├── Makefile ├── wmd-test.html ├── License.txt ├── Readme.md ├── markdownhelp.html ├── wmd.css ├── jsmin.py ├── showdown.js ├── wmd.combined.min.js └── wmd.js /wmd-buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChiperSoft/wmd/HEAD/wmd-buttons.png -------------------------------------------------------------------------------- /wmd-buttons.psd: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ChiperSoft/wmd/HEAD/wmd-buttons.psd -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | 2 | JSFILES=wmd.js showdown.js 3 | 4 | all: wmd.combined.js wmd.combined.min.js 5 | 6 | wmd.combined.js: $(JSFILES) 7 | cat $(JSFILES) > $@ 8 | 9 | wmd.combined.min.js: $(JSFILES) 10 | cat $(JSFILES) | python jsmin.py > $@ 11 | -------------------------------------------------------------------------------- /wmd-test.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | WMD Example 6 | 7 | 8 | 9 | 10 | 15 | 16 | 17 |

WMD Example

18 | 19 |
20 |
21 | 22 | 23 | Preview: 24 |
25 | 26 | Output: 27 | 28 |
29 | 30 | 41 | 42 | -------------------------------------------------------------------------------- /License.txt: -------------------------------------------------------------------------------- 1 | The MIT License 2 | 3 | Original WMD and Showdwon code copyright (c) 2007 John Fraser 4 | Modifications and bugfixes (c) 2009-2010 Chris Jester-Young, Dana Robinson, Anand Chitipothu 5 | Further modifications (c) 2010-2011 Jarvis Badgley, Wenqiang Wang, Helder Ribeiro, Chad Burggraf 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in 15 | all copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 23 | THE SOFTWARE. 24 | -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | WMD: The Wysiwym Markdown Editor 2 | ================================ 3 | 4 | Introduction 5 | ------------ 6 | 7 | WMD is a JavaScript based code editor for the [Markdown](http://daringfireball.net/projects/markdown/) formatting language. It includes a Markdown interpreter – Showdown – for live preview and output of the Markdown generated HTML. 8 | 9 | This is a fork of WMD for ChiperSoft Systems & NFY Interactive designed for use in CMS engines. It was forked from the [Open Library fork](http://github.com/openlibrary/wmd) of WMD, which was in turn forked from the [Stackoverflow fork](http://github.com/derobins/wmd). 10 | 11 | Major Changes from Open Library Revision 12 | ------------- 13 | 14 | * Extended showdown to support a series of Markdown extensions: 15 | - Link urls that start with ! are opened in a new window 16 | - Text wrapped with double carets is made superscript (ex: `^^this text is superscripted^^`) 17 | - Text wrapped with double commas is made subscript (ex: `,,this text is subscripted,,`) 18 | - Text wrapped with double tildes is made strikethrough (ex: `~~this text is struck~~`) 19 | - (c), (r), (tm), -- and ... are converted into their respective html entities. 20 | - Lines prefixed with "->" are right aligned. Lines also postfixed with "<-" are center aligned. 21 | * Several ascii characters that may produce encoding issues (such as curled quotes) are converted into entities 22 | * Removed top level frame pollution, forcing WMD to run only in its own document. 23 | * Removed the automatic conversion from Markdown to HTML when the form is submitted. 24 | * Removed the automatic addition of http:// to image urls, preventing the entry of relative addresses. 25 | * Numerous bug fixes to both WMD and Showdown 26 | 27 | How to use 28 | ---------- 29 | 30 | 31 | 32 | WMD Example 33 | 34 | 35 | 36 | 37 | 38 | 39 |

WMD Example

40 | 41 |
42 |
43 | 44 |
45 | 46 |
47 | 48 | 56 | 57 | 58 | 59 | License 60 | ------- 61 | 62 | WMD Editor is licensed under [MIT License](http://github.com/chipersoft/wmd/raw/master/License.txt). 63 | 64 | 65 | -------------------------------------------------------------------------------- /markdownhelp.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Markdown Help 6 | 14 | 15 | 16 | 17 |

Markdown Syntax Help

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 | 67 | 72 | 73 | 74 | 75 | 80 | 85 | 86 | 87 | 89 | 90 | 91 | 92 | 93 | 97 | 98 | 99 | 100 |
DescriptionExample:Creates:
Text surrounded by asterisks or underscores will be made italic*italics*italics
Text surrounded by double asterisks will be bold**bold**bold
To create a hyperlink, surround the text you want to be the link with brackets followed by the URL in parenthesis.[linked text](http://example.com)linked text
A line of text starting with a pound symbol will become a heading. There are six levels of heading which you can reach by adding more pound signs.#heading 1

heading 1

##heading 2

heading 2

You can also create a header by following it with two or more equal signs or hyphens.heading 1
===========

heading 1

heading 2
-----------

heading 2

A series of lines beginning with hyphens will create an unordered list. 63 | - item 1
64 | - item 2
65 | - item 3 66 |
    68 |
  • item 1
  • 69 |
  • item 2
  • 70 |
  • item 3
  • 71 |
A series of lines beginning with numbers followed by a period will create an ordered list. 76 | 1. item 1
77 | 2. item 2
78 | 3. item 3 79 |
    81 |
  1. item 1
  2. 82 |
  3. item 2
  4. 83 |
  5. item 3
  6. 84 |
A line started with a greater-then symbol will identify that text as being quoted from someone else. 88 | > quoted text
quoted text
Lines starting with four spaces are treated as preformatted text and will display without wrapping. 94 |     if 1 * 2 < 3:
95 |         print "hello, world!"
96 |
if 1 * 2 < 3:
print "hello, world!"
101 | 102 | -------------------------------------------------------------------------------- /wmd.css: -------------------------------------------------------------------------------- 1 | 2 | .wmd-panel 3 | { 4 | margin-left: 25%; 5 | margin-right: 25%; 6 | width: 50%; 7 | min-width: 500px; 8 | } 9 | 10 | #wmd-editor 11 | { 12 | background-color: Aquamarine; 13 | } 14 | 15 | .wmd-button-bar 16 | { 17 | width: 100%; 18 | /* 19 | background-color: Silver; 20 | */ 21 | } 22 | 23 | .wmd-input 24 | { 25 | height: 500px; 26 | width: 100%; 27 | background-color: Gainsboro; 28 | border: 1px solid DarkGray; 29 | } 30 | 31 | .wmd-preview 32 | { 33 | background-color: LightGray; 34 | } 35 | 36 | .wmd-output 37 | { 38 | background-color: Pink; 39 | } 40 | 41 | .wmd-button-row 42 | { 43 | position: relative; 44 | margin:0px; 45 | padding: 5px; 46 | height: 20px; 47 | } 48 | 49 | .wmd-spacer 50 | { 51 | width: 1px; 52 | height: 20px; 53 | margin-left: 7px; 54 | margin-right:7px; 55 | 56 | position: relative; 57 | background-color: Silver; 58 | display: inline-block; zoom:1; *display:inline; 59 | list-style: none; 60 | } 61 | 62 | .wmd-button 63 | { 64 | width: 20px; 65 | height: 20px; 66 | margin-left: 5px; 67 | margin-right: 5px; 68 | 69 | position: relative; 70 | background-image: url(wmd-buttons.png); 71 | background-repeat: no-repeat; 72 | background-position: 0px 0px; 73 | display: inline-block; zoom:1; *display:inline; 74 | list-style: none; 75 | } 76 | 77 | .wmd-button > a 78 | { 79 | width: 20px; 80 | height: 20px; 81 | margin-left: 5px; 82 | margin-right: 5px; 83 | 84 | position: absolute; 85 | display: inline-block; 86 | } 87 | 88 | 89 | /* sprite button slicing style information */ 90 | .wmd-bold-button {background-position: 0px 0px;} 91 | .wmd-bold-button:hover {background-position: 0px -40px;} 92 | .wmd-bold-button.disabled {background-position: 0px -20px;} 93 | 94 | .wmd-italic-button {background-position: -20px 0px;} 95 | .wmd-italic-button:hover {background-position: -20px -40px;} 96 | .wmd-italic-button.disabled {background-position: -20px -20px;} 97 | 98 | .wmd-link-button {background-position: -40px 0px;} 99 | .wmd-link-button:hover {background-position: -40px -40px;} 100 | .wmd-link-button.disabled {background-position: -40px -20px;} 101 | 102 | .wmd-quote-button {background-position: -60px 0px;} 103 | .wmd-quote-button:hover {background-position: -60px -40px;} 104 | .wmd-quote-button.disabled {background-position: -60px -20px;} 105 | 106 | .wmd-code-button {background-position: -80px 0px;} 107 | .wmd-code-button:hover {background-position: -80px -40px;} 108 | .wmd-code-button.disabled {background-position: -80px -20px;} 109 | 110 | .wmd-image-button {background-position: -100px 0px;} 111 | .wmd-image-button:hover {background-position: -100px -40px;} 112 | .wmd-image-button.disabled {background-position: -100px -20px;} 113 | 114 | .wmd-olist-button {background-position: -120px 0px;} 115 | .wmd-olist-button:hover {background-position: -120px -40px;} 116 | .wmd-olist-button.disabled {background-position: -120px -20px;} 117 | 118 | .wmd-ulist-button {background-position: -140px 0px;} 119 | .wmd-ulist-button:hover {background-position: -140px -40px;} 120 | .wmd-ulist-button.disabled {background-position: -140px -20px;} 121 | 122 | .wmd-heading-button {background-position: -160px 0px;} 123 | .wmd-heading-button:hover {background-position: -160px -40px;} 124 | .wmd-heading-button.disabled {background-position: -160px -20px;} 125 | 126 | .wmd-hr-button {background-position: -180px 0px;} 127 | .wmd-hr-button:hover {background-position: -180px -40px;} 128 | .wmd-hr-button.disabled {background-position: -180px -20px;} 129 | 130 | .wmd-undo-button {background-position: -200px 0px;} 131 | .wmd-undo-button:hover {background-position: -200px -40px;} 132 | .wmd-undo-button.disabled {background-position: -200px -20px;} 133 | 134 | .wmd-redo-button {background-position: -220px 0px;} 135 | .wmd-redo-button:hover {background-position: -220px -40px;} 136 | .wmd-redo-button.disabled {background-position: -220px -20px;} 137 | 138 | .wmd-help-button {background-position: -240px 0px;} 139 | .wmd-help-button:hover {background-position: -240px -40px;} 140 | .wmd-help-button.disabled {background-position: -240px -20px;} 141 | 142 | .wmd-help-button {position:absolute;top:5px;right: 0px; background-position: -240px 0;} 143 | 144 | 145 | .wmd-prompt-background 146 | { 147 | background-color: Black; 148 | } 149 | 150 | .wmd-prompt-dialog 151 | { 152 | border: 1px solid #999999; 153 | background-color: #F5F5F5; 154 | font-size: 0.8em; 155 | font-family: arial, helvetica, sans-serif; 156 | } 157 | 158 | .wmd-prompt-dialog > form > label { 159 | font-size: 0.6em; 160 | font-weight:bold; 161 | margin:4px 0; 162 | } 163 | 164 | 165 | .wmd-prompt-dialog > form > input[type="text"] { 166 | border: 1px solid #999999; 167 | color: black; 168 | } 169 | 170 | .wmd-prompt-dialog > form > input[type="button"]{ 171 | border: 1px solid #888888; 172 | font-family: trebuchet MS, helvetica, sans-serif; 173 | font-size: 0.8em; 174 | font-weight: bold; 175 | } 176 | -------------------------------------------------------------------------------- /jsmin.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/python 2 | 3 | # This code is original from jsmin by Douglas Crockford, it was translated to 4 | # Python by Baruch Even. The original code had the following copyright and 5 | # license. 6 | # 7 | # /* jsmin.c 8 | # 2007-05-22 9 | # 10 | # Copyright (c) 2002 Douglas Crockford (www.crockford.com) 11 | # 12 | # Permission is hereby granted, free of charge, to any person obtaining a copy of 13 | # this software and associated documentation files (the "Software"), to deal in 14 | # the Software without restriction, including without limitation the rights to 15 | # use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 16 | # of the Software, and to permit persons to whom the Software is furnished to do 17 | # so, subject to the following conditions: 18 | # 19 | # The above copyright notice and this permission notice shall be included in all 20 | # copies or substantial portions of the Software. 21 | # 22 | # The Software shall be used for Good, not Evil. 23 | # 24 | # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 25 | # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 26 | # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 27 | # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 28 | # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 29 | # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 30 | # SOFTWARE. 31 | # */ 32 | 33 | from StringIO import StringIO 34 | 35 | def jsmin(js): 36 | ins = StringIO(js) 37 | outs = StringIO() 38 | JavascriptMinify().minify(ins, outs) 39 | str = outs.getvalue() 40 | if len(str) > 0 and str[0] == '\n': 41 | str = str[1:] 42 | return str 43 | 44 | def isAlphanum(c): 45 | """return true if the character is a letter, digit, underscore, 46 | dollar sign, or non-ASCII character. 47 | """ 48 | return ((c >= 'a' and c <= 'z') or (c >= '0' and c <= '9') or 49 | (c >= 'A' and c <= 'Z') or c == '_' or c == '$' or c == '\\' or (c is not None and ord(c) > 126)); 50 | 51 | class UnterminatedComment(Exception): 52 | pass 53 | 54 | class UnterminatedStringLiteral(Exception): 55 | pass 56 | 57 | class UnterminatedRegularExpression(Exception): 58 | pass 59 | 60 | class JavascriptMinify(object): 61 | 62 | def _outA(self): 63 | self.outstream.write(self.theA) 64 | def _outB(self): 65 | self.outstream.write(self.theB) 66 | 67 | def _get(self): 68 | """return the next character from stdin. Watch out for lookahead. If 69 | the character is a control character, translate it to a space or 70 | linefeed. 71 | """ 72 | c = self.theLookahead 73 | self.theLookahead = None 74 | if c == None: 75 | c = self.instream.read(1) 76 | if c >= ' ' or c == '\n': 77 | return c 78 | if c == '': # EOF 79 | return '\000' 80 | if c == '\r': 81 | return '\n' 82 | return ' ' 83 | 84 | def _peek(self): 85 | self.theLookahead = self._get() 86 | return self.theLookahead 87 | 88 | def _next(self): 89 | """get the next character, excluding comments. peek() is used to see 90 | if an unescaped '/' is followed by a '/' or '*'. 91 | """ 92 | c = self._get() 93 | if c == '/' and self.theA != '\\': 94 | p = self._peek() 95 | if p == '/': 96 | c = self._get() 97 | while c > '\n': 98 | c = self._get() 99 | return c 100 | if p == '*': 101 | c = self._get() 102 | while 1: 103 | c = self._get() 104 | if c == '*': 105 | if self._peek() == '/': 106 | self._get() 107 | return ' ' 108 | if c == '\000': 109 | raise UnterminatedComment() 110 | 111 | return c 112 | 113 | def _action(self, action): 114 | """do something! What you do is determined by the argument: 115 | 1 Output A. Copy B to A. Get the next B. 116 | 2 Copy B to A. Get the next B. (Delete A). 117 | 3 Get the next B. (Delete B). 118 | action treats a string as a single character. Wow! 119 | action recognizes a regular expression if it is preceded by ( or , or =. 120 | """ 121 | if action <= 1: 122 | self._outA() 123 | 124 | if action <= 2: 125 | self.theA = self.theB 126 | if self.theA == "'" or self.theA == '"': 127 | while 1: 128 | self._outA() 129 | self.theA = self._get() 130 | if self.theA == self.theB: 131 | break 132 | if self.theA <= '\n': 133 | raise UnterminatedStringLiteral() 134 | if self.theA == '\\': 135 | self._outA() 136 | self.theA = self._get() 137 | 138 | 139 | if action <= 3: 140 | self.theB = self._next() 141 | if self.theB == '/' and (self.theA == '(' or self.theA == ',' or 142 | self.theA == '=' or self.theA == ':' or 143 | self.theA == '[' or self.theA == '?' or 144 | self.theA == '!' or self.theA == '&' or 145 | self.theA == '|' or self.theA == ';' or 146 | self.theA == '{' or self.theA == '}' or 147 | self.theA == '\n'): 148 | self._outA() 149 | self._outB() 150 | while 1: 151 | self.theA = self._get() 152 | if self.theA == '/': 153 | break 154 | elif self.theA == '\\': 155 | self._outA() 156 | self.theA = self._get() 157 | elif self.theA <= '\n': 158 | raise UnterminatedRegularExpression() 159 | self._outA() 160 | self.theB = self._next() 161 | 162 | 163 | def _jsmin(self): 164 | """Copy the input to the output, deleting the characters which are 165 | insignificant to JavaScript. Comments will be removed. Tabs will be 166 | replaced with spaces. Carriage returns will be replaced with linefeeds. 167 | Most spaces and linefeeds will be removed. 168 | """ 169 | self.theA = '\n' 170 | self._action(3) 171 | 172 | while self.theA != '\000': 173 | if self.theA == ' ': 174 | if isAlphanum(self.theB): 175 | self._action(1) 176 | else: 177 | self._action(2) 178 | elif self.theA == '\n': 179 | if self.theB in ['{', '[', '(', '+', '-']: 180 | self._action(1) 181 | elif self.theB == ' ': 182 | self._action(3) 183 | else: 184 | if isAlphanum(self.theB): 185 | self._action(1) 186 | else: 187 | self._action(2) 188 | else: 189 | if self.theB == ' ': 190 | if isAlphanum(self.theA): 191 | self._action(1) 192 | else: 193 | self._action(3) 194 | elif self.theB == '\n': 195 | if self.theA in ['}', ']', ')', '+', '-', '"', '\'']: 196 | self._action(1) 197 | else: 198 | if isAlphanum(self.theA): 199 | self._action(1) 200 | else: 201 | self._action(3) 202 | else: 203 | self._action(1) 204 | 205 | def minify(self, instream, outstream): 206 | self.instream = instream 207 | self.outstream = outstream 208 | self.theA = '\n' 209 | self.theB = None 210 | self.theLookahead = None 211 | 212 | self._jsmin() 213 | self.instream.close() 214 | 215 | if __name__ == '__main__': 216 | import sys 217 | jsm = JavascriptMinify() 218 | jsm.minify(sys.stdin, sys.stdout) 219 | -------------------------------------------------------------------------------- /showdown.js: -------------------------------------------------------------------------------- 1 | // 2 | // showdown.js -- A javascript port of Markdown. 3 | // 4 | // Copyright (c) 2010-2011 Jarvis Badgley . 5 | 6 | // Copyright (c) 2007 John Fraser . 7 | // 8 | // Original Markdown Copyright (c) 2004-2005 John Gruber 9 | // 10 | // 11 | // 12 | // Showdown usage: 13 | // 14 | // var text = "Markdown *rocks*."; 15 | // 16 | // var converter = new Showdown.converter(); 17 | // var html = converter.makeHtml(text); 18 | // 19 | // alert(html); 20 | // 21 | // Note: move the sample code to the bottom of this 22 | // file before uncommenting it. 23 | // 24 | 25 | // 26 | // Showdown namespace 27 | // 28 | Showdown = {}; 29 | 30 | // 31 | // converter 32 | // 33 | // Wraps all "globals" so that the only thing 34 | // exposed is makeHtml(). 35 | // 36 | Showdown.converter = function () { 37 | 38 | // 39 | // Globals: 40 | // 41 | // Global hashes, used by various utility routines 42 | var g_urls; 43 | var g_titles; 44 | var g_html_blocks; 45 | 46 | // Used to track when we're inside an ordered or unordered list 47 | // (see _ProcessListItems() for details): 48 | var g_list_level = 0; 49 | 50 | 51 | this.makeHtml = function (text) { 52 | // 53 | // Main function. The order in which other subs are called here is 54 | // essential. Link and image substitutions need to happen before 55 | // _EscapeSpecialCharsWithinTagAttributes(), so that any *'s or _'s in the 56 | // and tags get encoded. 57 | // 58 | // Clear the global hashes. If we don't clear these, you get conflicts 59 | // from other articles when generating a page which contains more than 60 | // one article (e.g. an index page that shows the N most recent 61 | // articles): 62 | g_urls = []; 63 | g_titles = []; 64 | g_html_blocks = []; 65 | 66 | // attacklab: Replace ~ with ~T 67 | // This lets us use tilde as an escape char to avoid md5 hashes 68 | // The choice of character is arbitray; anything that isn't 69 | // magic in Markdown will work. 70 | text = text.replace(/~/g, "~T"); 71 | 72 | // attacklab: Replace $ with ~D 73 | // RegExp interprets $ as a special character 74 | // when it's in a replacement string 75 | text = text.replace(/\$/g, "~D"); 76 | 77 | // Standardize line endings 78 | text = text.replace(/\r\n/g, "\n"); // DOS to Unix 79 | text = text.replace(/\r/g, "\n"); // Mac to Unix 80 | // Make sure text begins and ends with a couple of newlines: 81 | text = "\n\n" + text + "\n\n"; 82 | 83 | // Convert all tabs to spaces. 84 | text = _Detab(text); 85 | 86 | // Strip any lines consisting only of spaces and tabs. 87 | // This makes subsequent regexen easier to write, because we can 88 | // match consecutive blank lines with /\n+/ instead of something 89 | // contorted like /[ \t]*\n+/ . 90 | text = text.replace(/^[ \t]+$/mg, ""); 91 | 92 | // Turn block-level HTML blocks into hash entries 93 | text = _HashHTMLBlocks(text); 94 | 95 | // Strip link definitions, store in hashes. 96 | text = _StripLinkDefinitions(text); 97 | 98 | text = _RunBlockGamut(text); 99 | 100 | text = _UnescapeSpecialChars(text); 101 | 102 | // attacklab: Restore dollar signs 103 | text = text.replace(/~D/g, "$$"); 104 | 105 | // attacklab: Restore tildes 106 | text = text.replace(/~T/g, "~"); 107 | 108 | // ** GFM ** Auto-link URLs and emails 109 | text = text.replace(/https?\:\/\/[^"\s<>]*[^.,;'">\:\s<>\)\]\!]/g, function (wholeMatch, matchIndex){ 110 | var left = text.slice(Math.max(0,text.lastIndexOf('\n',matchIndex)), matchIndex); 111 | var right = text.slice(matchIndex); 112 | if (left.match(/<([a-z]+)\s[^>]+>?$/) && right.match(/^[^>]*>/)) {return wholeMatch;} 113 | return "" + wholeMatch + ""; 114 | }); 115 | text = text.replace(/[a-z0-9_\-+=.]+@[a-z0-9\-]+(\.[a-z0-9\-]+)+/ig, function (wholeMatch, m1, matchIndex) { 116 | var left = text.slice(Math.max(0,text.lastIndexOf('\n',matchIndex)), matchIndex); 117 | if (left.match(/<([a-z]+)\s[^>]+>?$/) || left.match(/mailto\:$/)) {return wholeMatch;} 118 | return "" + wholeMatch + ""; 119 | }); 120 | 121 | 122 | return text; 123 | }; 124 | 125 | var _StripLinkDefinitions = function (text) { 126 | // 127 | // Strips link definitions from text, stores the URLs and titles in 128 | // hash references. 129 | // 130 | // Link defs are in the form: ^[id]: url "optional title" 131 | /* 132 | var text = text.replace(/ 133 | ^[ ]{0,3}\[(.+)\]: // id = $1 attacklab: g_tab_width - 1 134 | [ \t]* 135 | \n? // maybe *one* newline 136 | [ \t]* 137 | ? // url = $2 138 | [ \t]* 139 | \n? // maybe one newline 140 | [ \t]* 141 | (?: 142 | (\n*) // any lines skipped = $3 attacklab: lookbehind removed 143 | ["(] 144 | (.+?) // title = $4 145 | [")] 146 | [ \t]* 147 | )? // title is optional 148 | (?:\n+|$) 149 | /gm, 150 | function(){...}); 151 | */ 152 | text = text.replace(/^[ ]{0,3}\[(.+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+)/gm, function (wholeMatch, m1, m2, m3, m4) { 153 | m1 = m1.toLowerCase(); 154 | g_urls[m1] = _EncodeAmpsAndAngles(m2); // Link IDs are case-insensitive 155 | if (m3) { 156 | // Oops, found blank lines, so it's not a title. 157 | // Put back the parenthetical statement we stole. 158 | return m3 + m4; 159 | } else if (m4) { 160 | g_titles[m1] = m4.replace(/"/g, """); 161 | } 162 | 163 | // Completely remove the definition from the text 164 | return ""; 165 | }); 166 | 167 | return text; 168 | }; 169 | 170 | var _HashHTMLBlocks = function (text) { 171 | // attacklab: Double up blank lines to reduce lookaround 172 | text = text.replace(/\n/g, "\n\n"); 173 | 174 | // Hashify HTML blocks: 175 | // We only want to do this for block-level HTML tags, such as headers, 176 | // lists, and tables. That's because we still want to wrap

s around 177 | // "paragraphs" that are wrapped in non-block-level tags, such as anchors, 178 | // phrase emphasis, and spans. The list of tags we're looking for is 179 | // hard-coded: 180 | var block_tags_a = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del"; 181 | var block_tags_b = "p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math"; 182 | 183 | // First, look for nested blocks, e.g.: 184 | //

185 | //
186 | // tags for inner block must be indented. 187 | //
188 | //
189 | // 190 | // The outermost tags must start at the left margin for this to match, and 191 | // the inner nested divs must be indented. 192 | // We need to do this before the next, more liberal match, because the next 193 | // match will start at the first `
` and stop at the first `
`. 194 | // attacklab: This regex can be expensive when it fails. 195 | /* 196 | var text = text.replace(/ 197 | ( // save in $1 198 | ^ // start of line (with /m) 199 | <($block_tags_a) // start tag = $2 200 | \b // word break 201 | // attacklab: hack around khtml/pcre bug... 202 | [^\r]*?\n // any number of lines, minimally matching 203 | // the matching end tag 204 | [ \t]* // trailing spaces/tabs 205 | (?=\n+) // followed by a newline 206 | ) // attacklab: there are sentinel newlines at end of document 207 | /gm,function(){...}}; 208 | */ 209 | text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math|ins|del)\b[^\r]*?\n<\/\2>[ \t]*(?=\n+))/gm, hashElement); 210 | 211 | // 212 | // Now match more liberally, simply from `\n` to `\n` 213 | // 214 | /* 215 | var text = text.replace(/ 216 | ( // save in $1 217 | ^ // start of line (with /m) 218 | <($block_tags_b) // start tag = $2 219 | \b // word break 220 | // attacklab: hack around khtml/pcre bug... 221 | [^\r]*? // any number of lines, minimally matching 222 | .* // the matching end tag 223 | [ \t]* // trailing spaces/tabs 224 | (?=\n+) // followed by a newline 225 | ) // attacklab: there are sentinel newlines at end of document 226 | /gm,function(){...}}; 227 | */ 228 | text = text.replace(/^(<(p|div|h[1-6]|blockquote|pre|table|dl|ol|ul|script|noscript|form|fieldset|iframe|math)\b[^\r]*?.*<\/\2>[ \t]*(?=\n+)\n)/gm, hashElement); 229 | 230 | // Special case just for
. It was easier to make a special case than 231 | // to make the other regex more complicated. 232 | /* 233 | text = text.replace(/ 234 | ( // save in $1 235 | \n\n // Starting after a blank line 236 | [ ]{0,3} 237 | (<(hr) // start tag = $2 238 | \b // word break 239 | ([^<>])*? // 240 | \/?>) // the matching end tag 241 | [ \t]* 242 | (?=\n{2,}) // followed by a blank line 243 | ) 244 | /g,hashElement); 245 | */ 246 | text = text.replace(/(\n[ ]{0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g, hashElement); 247 | 248 | // Special case for standalone HTML comments: 249 | /* 250 | text = text.replace(/ 251 | ( // save in $1 252 | \n\n // Starting after a blank line 253 | [ ]{0,3} // attacklab: g_tab_width - 1 254 | 257 | [ \t]* 258 | (?=\n{2,}) // followed by a blank line 259 | ) 260 | /g,hashElement); 261 | */ 262 | text = text.replace(/(\n\n[ ]{0,3}[ \t]*(?=\n{2,}))/g, hashElement); 263 | 264 | // PHP and ASP-style processor instructions ( and <%...%>) 265 | /* 266 | text = text.replace(/ 267 | (?: 268 | \n\n // Starting after a blank line 269 | ) 270 | ( // save in $1 271 | [ ]{0,3} // attacklab: g_tab_width - 1 272 | (?: 273 | <([?%]) // $2 274 | [^\r]*? 275 | \2> 276 | ) 277 | [ \t]* 278 | (?=\n{2,}) // followed by a blank line 279 | ) 280 | /g,hashElement); 281 | */ 282 | text = text.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g, hashElement); 283 | 284 | // attacklab: Undo double lines (see comment at top of this function) 285 | text = text.replace(/\n\n/g, "\n"); 286 | return text; 287 | }; 288 | 289 | var hashElement = function (wholeMatch, m1) { 290 | var blockText = m1; 291 | 292 | // Undo double lines 293 | blockText = blockText.replace(/\n\n/g, "\n"); 294 | blockText = blockText.replace(/^\n/, ""); 295 | 296 | // strip trailing blank lines 297 | blockText = blockText.replace(/\n+$/g, ""); 298 | 299 | // Replace the element text with a marker ("~KxK" where x is its key) 300 | blockText = "\n\n~K" + (g_html_blocks.push(blockText) - 1) + "K\n\n"; 301 | 302 | return blockText; 303 | }; 304 | 305 | var _RunBlockGamut = function (text) { 306 | // 307 | // These are all the transformations that form block-level 308 | // tags like paragraphs, headers, and list items. 309 | // 310 | text = _DoHeaders(text); 311 | 312 | // Do Horizontal Rules: 313 | var key = hashBlock("
"); 314 | text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, key); 315 | text = text.replace(/^[ ]{0,2}([ ]?\-[ ]?){3,}[ \t]*$/gm, key); 316 | text = text.replace(/^[ ]{0,2}([ ]?\_[ ]?){3,}[ \t]*$/gm, key); 317 | 318 | text = _DoLists(text); 319 | text = _DoCodeBlocks(text); 320 | text = _DoBlockQuotes(text); 321 | 322 | // We already ran _HashHTMLBlocks() before, in Markdown(), but that 323 | // was to escape raw HTML in the original Markdown source. This time, 324 | // we're escaping the markup we've just created, so that we don't wrap 325 | //

tags around block-level tags. 326 | text = _HashHTMLBlocks(text); 327 | text = _FormParagraphs(text); 328 | 329 | return text; 330 | }; 331 | 332 | 333 | var _RunSpanGamut = function (text) { 334 | // 335 | // These are all the transformations that occur *within* block-level 336 | // tags like paragraphs, headers, and list items. 337 | // 338 | text = _DoCodeSpans(text); 339 | text = _EscapeSpecialCharsWithinTagAttributes(text); 340 | text = _EncodeBackslashEscapes(text); 341 | 342 | // Process anchor and image tags. Images must come first, 343 | // because ![foo][f] looks like an anchor. 344 | text = _DoImages(text); 345 | text = _DoAnchors(text); 346 | 347 | // Make links out of things like `` 348 | // Must come after _DoAnchors(), because you can use < and > 349 | // delimiters in inline links like [this](). 350 | text = _DoAutoLinks(text); 351 | text = _EncodeAmpsAndAngles(text); 352 | text = _ConvertExtraSpecialCharacters(text); 353 | text = _DoItalicsAndBold(text); 354 | 355 | // Do hard breaks: 356 | text = text.replace(/ {2,}\n/g, "
\n"); 357 | 358 | return text; 359 | }; 360 | 361 | var _EscapeSpecialCharsWithinTagAttributes = function (text) { 362 | // 363 | // Within tags -- meaning between < and > -- encode [\ ` * _] so they 364 | // don't conflict with their use in Markdown for code, italics and strong. 365 | // 366 | // Build a regex to find HTML tags and comments. See Friedl's 367 | // "Mastering Regular Expressions", 2nd Ed., pp. 200-201. 368 | var regex = /(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi; 369 | 370 | text = text.replace(regex, function (wholeMatch) { 371 | var tag = wholeMatch.replace(/(.)<\/?code>(?=.)/g, "$1`"); 372 | tag = escapeCharacters(tag, "\\`*_"); 373 | return tag; 374 | }); 375 | 376 | return text; 377 | }; 378 | 379 | var _DoAnchors = function (text) { 380 | // 381 | // Turn Markdown link shortcuts into XHTML tags. 382 | // 383 | // 384 | // First, handle reference-style links: [link text] [id] 385 | // 386 | /* 387 | text = text.replace(/ 388 | ( // wrap whole match in $1 389 | \[ 390 | ( 391 | (?: 392 | \[[^\]]*\] // allow brackets nested one level 393 | | 394 | [^\[] // or anything else 395 | )* 396 | ) 397 | \] 398 | 399 | [ ]? // one optional space 400 | (?:\n[ ]*)? // one optional newline followed by spaces 401 | 402 | \[ 403 | (.*?) // id = $3 404 | \] 405 | )()()()() // pad remaining backreferences 406 | /g,_DoAnchors_callback); 407 | */ 408 | text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeAnchorTag); 409 | 410 | // 411 | // Next, inline-style links: [link text](url "optional title") 412 | // 413 | /* 414 | text = text.replace(/ 415 | ( // wrap whole match in $1 416 | \[ 417 | ( 418 | (?: 419 | \[[^\]]*\] // allow brackets nested one level 420 | | 421 | [^\[\]] // or anything else 422 | ) 423 | ) 424 | \] 425 | \( // literal paren 426 | [ \t]* 427 | () // no id, so leave $3 empty 428 | ? // href = $4 429 | [ \t]* 430 | ( // $5 431 | (['"]) // quote char = $6 432 | (.*?) // Title = $7 433 | \6 // matching quote 434 | [ \t]* // ignore any spaces/tabs between closing quote and ) 435 | )? // title is optional 436 | \) 437 | ) 438 | /g,writeAnchorTag); 439 | */ 440 | text = text.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeAnchorTag); 441 | 442 | // 443 | // Last, handle reference-style shortcuts: [link text] 444 | // These must come last in case you've also got [link test][1] 445 | // or [link test](/foo) 446 | // 447 | /* 448 | text = text.replace(/ 449 | ( // wrap whole match in $1 450 | \[ 451 | ([^\[\]]+) // link text = $2; can't contain '[' or ']' 452 | \] 453 | )()()()()() // pad rest of backreferences 454 | /g, writeAnchorTag); 455 | */ 456 | text = text.replace(/(\[([^\[\]]+)\])()()()()()/g, writeAnchorTag); 457 | 458 | return text; 459 | }; 460 | 461 | var writeAnchorTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) { 462 | if (m7 === undefined) m7 = ""; 463 | var whole_match = m1; 464 | var link_text = m2; 465 | var link_id = m3.toLowerCase(); 466 | var url = m4; 467 | var title = m7; 468 | var blank_target = false; 469 | 470 | if (url === "") { 471 | if (link_id === "") { 472 | // lower-case and turn embedded newlines into spaces 473 | link_id = link_text.toLowerCase().replace(/ ?\n/g, " "); 474 | } else { 475 | if (link_id[0]=="!") { 476 | blank_target = true; 477 | link_id = link_id.substr(1); 478 | } 479 | } 480 | url = "#" + link_id; 481 | 482 | if (g_urls[link_id] !== undefined) { 483 | url = g_urls[link_id]; 484 | if (g_titles[link_id] !== undefined) { 485 | title = g_titles[link_id]; 486 | } 487 | if (url[0]=="!") { 488 | blank_target = true; 489 | url = url.substr(1); 490 | } 491 | } 492 | else { 493 | if (whole_match.search(/\(\s*\)$/m) > -1) { 494 | // Special case for explicit empty url 495 | url = ""; 496 | } else { 497 | return whole_match; 498 | } 499 | } 500 | } else { 501 | if (url[0]=="!") { 502 | blank_target = true; 503 | url = url.substr(1); 504 | } 505 | } 506 | 507 | url = escapeCharacters(url, "*_"); 508 | var result = ""; 521 | 522 | return result; 523 | }; 524 | 525 | 526 | var _DoImages = function (text) { 527 | // 528 | // Turn Markdown image shortcuts into tags. 529 | // 530 | // 531 | // First, handle reference-style labeled images: ![alt text][id] 532 | // 533 | /* 534 | text = text.replace(/ 535 | ( // wrap whole match in $1 536 | !\[ 537 | (.*?) // alt text = $2 538 | \] 539 | 540 | [ ]? // one optional space 541 | (?:\n[ ]*)? // one optional newline followed by spaces 542 | 543 | \[ 544 | (.*?) // id = $3 545 | \] 546 | )()()()() // pad rest of backreferences 547 | /g,writeImageTag); 548 | */ 549 | text = text.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g, writeImageTag); 550 | 551 | // 552 | // Next, handle inline images: ![alt text](url "optional title") 553 | // Don't forget: encode * and _ 554 | /* 555 | text = text.replace(/ 556 | ( // wrap whole match in $1 557 | !\[ 558 | (.*?) // alt text = $2 559 | \] 560 | \s? // One optional whitespace character 561 | \( // literal paren 562 | [ \t]* 563 | () // no id, so leave $3 empty 564 | ? // src url = $4 565 | [ \t]* 566 | ( // $5 567 | (['"]) // quote char = $6 568 | (.*?) // title = $7 569 | \6 // matching quote 570 | [ \t]* 571 | )? // title is optional 572 | \) 573 | ) 574 | /g,writeImageTag); 575 | */ 576 | text = text.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g, writeImageTag); 577 | 578 | return text; 579 | }; 580 | 581 | var writeImageTag = function (wholeMatch, m1, m2, m3, m4, m5, m6, m7) { 582 | var whole_match = m1; 583 | var alt_text = m2; 584 | var link_id = m3.toLowerCase(); 585 | var url = m4; 586 | var title = m7; 587 | 588 | if (!title) title = ""; 589 | 590 | if (url === "") { 591 | if (link_id === "") { 592 | // lower-case and turn embedded newlines into spaces 593 | link_id = alt_text.toLowerCase().replace(/ ?\n/g, " "); 594 | } 595 | url = "#" + link_id; 596 | 597 | if (g_urls[link_id] !== undefined) { 598 | url = g_urls[link_id]; 599 | if (g_titles[link_id] !== undefined) { 600 | title = g_titles[link_id]; 601 | } 602 | } 603 | else { 604 | return whole_match; 605 | } 606 | } 607 | 608 | alt_text = alt_text.replace(/"/g, """); 609 | url = escapeCharacters(url, "*_"); 610 | var result = "\""" + _RunSpanGamut(m1) + ""); 636 | }); 637 | 638 | text = text.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm, function (matchFound, m1) { 639 | return hashBlock("

" + _RunSpanGamut(m1) + "

"); 640 | }); 641 | 642 | // atx-style headers: 643 | // # Header 1 644 | // ## Header 2 645 | // ## Header 2 with closing hashes ## 646 | // ... 647 | // ###### Header 6 648 | // 649 | /* 650 | text = text.replace(/ 651 | ^(\#{1,6}) // $1 = string of #'s 652 | [ \t]* 653 | (.+?) // $2 = Header text 654 | [ \t]* 655 | \#* // optional closing #'s (not counted) 656 | \n+ 657 | /gm, function() {...}); 658 | */ 659 | 660 | text = text.replace(/^(\#{1,6})[ \t]*(.+?)[ \t]*\#*\n+/gm, function (wholeMatch, m1, m2) { 661 | var h_level = m1.length; 662 | return hashBlock("" + _RunSpanGamut(m2) + ""); 663 | }); 664 | 665 | return text; 666 | }; 667 | 668 | // This declaration keeps Dojo compressor from outputting garbage: 669 | var _ProcessListItems; 670 | 671 | var _DoLists = function (text) { 672 | // 673 | // Form HTML ordered (numbered) and unordered (bulleted) lists. 674 | // 675 | // attacklab: add sentinel to hack around khtml/safari bug: 676 | // http://bugs.webkit.org/show_bug.cgi?id=11231 677 | text += "~0"; 678 | 679 | // Re-usable pattern to match any entirel ul or ol list: 680 | /* 681 | var whole_list = / 682 | ( // $1 = whole list 683 | ( // $2 684 | [ ]{0,3} // attacklab: g_tab_width - 1 685 | ([*+-]|\d+[.]) // $3 = first list item marker 686 | [ \t]+ 687 | ) 688 | [^\r]+? 689 | ( // $4 690 | ~0 // sentinel for workaround; should be $ 691 | | 692 | \n{2,} 693 | (?=\S) 694 | (?! // Negative lookahead for another list item marker 695 | [ \t]* 696 | (?:[*+-]|\d+[.])[ \t]+ 697 | ) 698 | ) 699 | )/g 700 | */ 701 | var whole_list = /^(([ ]{0,3}([*+\-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+\-]|\d+[.])[ \t]+)))/gm; 702 | 703 | if (g_list_level) { 704 | text = text.replace(whole_list, function (wholeMatch, m1, m2) { 705 | var list = m1; 706 | var list_type = (m2.search(/[*+\-]/g) > -1) ? "ul" : "ol"; 707 | 708 | // Turn double returns into triple returns, so that we can make a 709 | // paragraph for the last item in a list, if necessary: 710 | list = list.replace(/\n{2,}/g, "\n\n\n"); 711 | var result = _ProcessListItems(list); 712 | 713 | // Trim any trailing whitespace, to put the closing `` 714 | // up on the preceding line, to get it past the current stupid 715 | // HTML block parser. This is a hack to work around the terrible 716 | // hack that is the HTML block parser. 717 | result = result.replace(/\s+$/, ""); 718 | result = "<" + list_type + ">" + result + "\n"; 719 | return result; 720 | }); 721 | } else { 722 | whole_list = /(\n\n|^\n?)(([ ]{0,3}([*+\-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+\-]|\d+[.])[ \t]+)))/g; 723 | text = text.replace(whole_list, function (wholeMatch, m1, m2, m3) { 724 | var runup = m1; 725 | var list = m2; 726 | 727 | var list_type = (m3.search(/[*+\-]/g) > -1) ? "ul" : "ol"; 728 | // Turn double returns into triple returns, so that we can make a 729 | // paragraph for the last item in a list, if necessary: 730 | list = list.replace(/\n{2,}/g, "\n\n\n"); 731 | var result = _ProcessListItems(list); 732 | result = runup + "<" + list_type + ">\n" + result + "\n"; 733 | return result; 734 | }); 735 | } 736 | 737 | // attacklab: strip sentinel 738 | text = text.replace(/~0/, ""); 739 | 740 | return text; 741 | }; 742 | 743 | _ProcessListItems = function (list_str) { 744 | // 745 | // Process the contents of a single ordered or unordered list, splitting it 746 | // into individual list items. 747 | // 748 | // The $g_list_level global keeps track of when we're inside a list. 749 | // Each time we enter a list, we increment it; when we leave a list, 750 | // we decrement. If it's zero, we're not in a list anymore. 751 | // 752 | // We do this because when we're not inside a list, we want to treat 753 | // something like this: 754 | // 755 | // I recommend upgrading to version 756 | // 8. Oops, now this line is treated 757 | // as a sub-list. 758 | // 759 | // As a single paragraph, despite the fact that the second line starts 760 | // with a digit-period-space sequence. 761 | // 762 | // Whereas when we're inside a list (or sub-list), that line will be 763 | // treated as the start of a sub-list. What a kludge, huh? This is 764 | // an aspect of Markdown's syntax that's hard to parse perfectly 765 | // without resorting to mind-reading. Perhaps the solution is to 766 | // change the syntax rules such that sub-lists must start with a 767 | // starting cardinal number; e.g. "1." or "a.". 768 | g_list_level++; 769 | 770 | // trim trailing blank lines: 771 | list_str = list_str.replace(/\n{2,}$/, "\n"); 772 | 773 | // attacklab: add sentinel to emulate \z 774 | list_str += "~0"; 775 | 776 | /* 777 | list_str = list_str.replace(/ 778 | (\n)? // leading line = $1 779 | (^[ \t]*) // leading whitespace = $2 780 | ([*+-]|\d+[.]) [ \t]+ // list marker = $3 781 | ([^\r]+? // list item text = $4 782 | (\n{1,2})) 783 | (?= \n* (~0 | \2 ([*+-]|\d+[.]) [ \t]+)) 784 | /gm, function(){...}); 785 | */ 786 | list_str = list_str.replace(/(\n)?(^[ \t]*)([*+\-]|\d+[.])[ \t]+([^\r]+?(\n{1,2}))(?=\n*(~0|\2([*+\-]|\d+[.])[ \t]+))/gm, function (wholeMatch, m1, m2, m3, m4) { 787 | var item = m4; 788 | var leading_line = m1; 789 | var leading_space = m2; 790 | 791 | if (leading_line || (item.search(/\n{2,}/) > -1)) { 792 | item = _RunBlockGamut(_Outdent(item)); 793 | } 794 | else { 795 | // Recursion for sub-lists: 796 | item = _DoLists(_Outdent(item)); 797 | item = item.replace(/\n$/, ""); // chomp(item) 798 | item = _RunSpanGamut(item); 799 | } 800 | 801 | return "
  • " + item + "
  • \n"; 802 | }); 803 | 804 | // attacklab: strip sentinel 805 | list_str = list_str.replace(/~0/g, ""); 806 | 807 | g_list_level--; 808 | return list_str; 809 | }; 810 | 811 | 812 | var _DoCodeBlocks = function (text) { 813 | // 814 | // Process Markdown `
    ` blocks.
     815 | 		//  
     816 | /*
     817 | 		text = text.replace(text,
     818 | 			/(?:\n\n|^)
     819 | 			(								// $1 = the code block -- one or more lines, starting with a space/tab
     820 | 				(?:
     821 | 					(?:[ ]{4}|\t)			// Lines must start with a tab or a tab-width of spaces - attacklab: g_tab_width
     822 | 					.*\n+
     823 | 				)+
     824 | 			)
     825 | 			(\n*[ ]{0,3}[^ \t\n]|(?=~0))	// attacklab: g_tab_width
     826 | 		/g,function(){...});
     827 | 	*/
     828 | 
     829 | 		// attacklab: sentinel workarounds for lack of \A and \Z, safari\khtml bug
     830 | 		text += "~0";
     831 | 
     832 | 		text = text.replace(/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g, function (wholeMatch, m1, m2) {
     833 | 			var codeblock = m1;
     834 | 			var nextChar = m2;
     835 | 
     836 | 			codeblock = _EncodeCode(_Outdent(codeblock));
     837 | 			codeblock = _Detab(codeblock);
     838 | 			codeblock = codeblock.replace(/^\n+/g, ""); // trim leading newlines
     839 | 			codeblock = codeblock.replace(/\n+$/g, ""); // trim trailing whitespace
     840 | 			codeblock = "
    " + codeblock + "\n
    "; 841 | 842 | return hashBlock(codeblock) + nextChar; 843 | }); 844 | 845 | // attacklab: strip sentinel 846 | text = text.replace(/~0/, ""); 847 | 848 | return text; 849 | }; 850 | 851 | var hashBlock = function (text) { 852 | text = text.replace(/(^\n+|\n+$)/g, ""); 853 | return "\n\n~K" + (g_html_blocks.push(text) - 1) + "K\n\n"; 854 | }; 855 | 856 | 857 | var _DoCodeSpans = function (text) { 858 | // 859 | // * Backtick quotes are used for spans. 860 | // 861 | // * You can use multiple backticks as the delimiters if you want to 862 | // include literal backticks in the code span. So, this input: 863 | // 864 | // Just type ``foo `bar` baz`` at the prompt. 865 | // 866 | // Will translate to: 867 | // 868 | //

    Just type foo `bar` baz at the prompt.

    869 | // 870 | // There's no arbitrary limit to the number of backticks you 871 | // can use as delimters. If you need three consecutive backticks 872 | // in your code, use four for delimiters, etc. 873 | // 874 | // * You can use spaces to get literal backticks at the edges: 875 | // 876 | // ... type `` `bar` `` ... 877 | // 878 | // Turns to: 879 | // 880 | // ... type `bar` ... 881 | // 882 | /* 883 | text = text.replace(/ 884 | (^|[^\\]) // Character before opening ` can't be a backslash 885 | (`+) // $2 = Opening run of ` 886 | ( // $3 = The code block 887 | [^\r]*? 888 | [^`] // attacklab: work around lack of lookbehind 889 | ) 890 | \2 // Matching closer 891 | (?!`) 892 | /gm, function(){...}); 893 | */ 894 | 895 | text = text.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm, function (wholeMatch, m1, m2, m3, m4) { 896 | var c = m3; 897 | c = c.replace(/^([ \t]*)/g, ""); // leading whitespace 898 | c = c.replace(/[ \t]*$/g, ""); // trailing whitespace 899 | c = _EncodeCode(c); 900 | return m1 + "" + c + ""; 901 | }); 902 | 903 | 904 | // Process ^^superscript^^ notation 905 | text = text.replace(/(^|[^\\])(\^{2})([^\r]*?[^\^]{2})\2(?!\^)/gm, function (wholeMatch, m1, m2, m3, m4) { 906 | var c = m3; 907 | c = c.replace(/^([ \t]*)/g, ""); // leading whitespace 908 | c = c.replace(/[ \t]*$/g, ""); // trailing whitespace 909 | c = _EncodeCode(c); 910 | return m1 + "" + c + ""; 911 | }); 912 | 913 | // Process ,,subscript,, notation 914 | text = text.replace(/(^|[^\\])(,{2})([^\r]*?[^,]{2})\2(?!,)/gm, function (wholeMatch, m1, m2, m3, m4) { 915 | var c = m3; 916 | c = c.replace(/^([ \t]*)/g, ""); // leading whitespace 917 | c = c.replace(/[ \t]*$/g, ""); // trailing whitespace 918 | c = _EncodeCode(c); 919 | return m1 + "" + c + ""; 920 | }); 921 | 922 | // Process ~~strike~~ notation 923 | text = text.replace(/(^|[^\\])(~T~T)([^\r]*?[^~]{2})\2(?!~)/gm, function (wholeMatch, m1, m2, m3, m4) { 924 | var c = m3; 925 | c = c.replace(/^([ \t]*)/g, ""); // leading whitespace 926 | c = c.replace(/[ \t]*$/g, ""); // trailing whitespace 927 | c = _EncodeCode(c); 928 | return m1 + "" + c + ""; 929 | }); 930 | 931 | return text; 932 | }; 933 | 934 | 935 | var _EncodeCode = function (text) { 936 | // 937 | // Encode/escape certain characters inside Markdown code runs. 938 | // The point is that in code, these characters are literals, 939 | // and lose their special Markdown meanings. 940 | // 941 | // Encode all ampersands; HTML entities are not 942 | // entities within a Markdown code span. 943 | text = text.replace(/&/g, "&"); 944 | 945 | // Do the angle bracket song and dance: 946 | text = text.replace(//g, ">"); 948 | 949 | // Encode "smart" quotes 950 | text = text. 951 | replace( /\u2026/g , '…'). 952 | replace( /\u00AB/g , '«' ). 953 | replace( /\u00BB/g , '»' ). 954 | replace( /\u201C/g , '“' ). 955 | replace( /\u201D/g , '”' ). 956 | replace( /\u2018/g , '‘' ). 957 | replace( /\u2019/g , '’' ). 958 | replace( /\u2014/g , '—' ). 959 | replace( /\u2013/g , '–' ). 960 | replace( /\u2022/g , '•' ). 961 | replace( /\u2122/g , '™' ). 962 | replace( /\u00A9/g , '©' ). 963 | replace( /\u00AE/g , '®' ); 964 | 965 | 966 | // Now, escape characters that are magic in Markdown: 967 | text = escapeCharacters(text, "*_{}[]\\", false); 968 | 969 | // jj the line above breaks this: 970 | //--- 971 | //* Item 972 | // 1. Subitem 973 | // special char: * 974 | //--- 975 | return text; 976 | }; 977 | 978 | 979 | var _DoItalicsAndBold = function (text) { 980 | 981 | if (true) { //eventually this will be replaced with a runtime option. But for now we're forcing it. 982 | text = text.replace(/(\*\*)(?=\S)([^\r]*?\S[*]*)\1/g, "$2"); 983 | text = text.replace(/(\w)_(\w)/g, "$1~E95E$2"); // ** GFM ** "~E95E" == escaped "_" 984 | text = text.replace(/(\*)(?=\S)([^\r]*?\S)\1/g, "$2"); 985 | text = text.replace(/(_)(?=\S)([^\r]*?\S)\1/g, "$2"); 986 | } else { 987 | // must go first: 988 | text = text.replace(/(\*\*|__)(?=\S)([^\r]*?\S[*_]*)\1/g, "$2"); 989 | 990 | text = text.replace(/(\w)_(\w)/g, "$1~E95E$2"); // ** GFM ** "~E95E" == escaped "_" 991 | text = text.replace(/(\*|_)(?=\S)([^\r]*?\S)\1/g, "$2"); 992 | } 993 | 994 | return text; 995 | }; 996 | 997 | 998 | var _DoBlockQuotes = function (text) { 999 | 1000 | /* 1001 | text = text.replace(/ 1002 | ( // Wrap whole match in $1 1003 | ( 1004 | ^[ \t]*>[ \t]? // '>' at the start of a line 1005 | .+\n // rest of the first line 1006 | (.+\n)* // subsequent consecutive lines 1007 | \n* // blanks 1008 | )+ 1009 | ) 1010 | /gm, function(){...}); 1011 | */ 1012 | 1013 | text = text.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm, function (wholeMatch, m1) { 1014 | var bq = m1; 1015 | 1016 | // attacklab: hack around Konqueror 3.5.4 bug: 1017 | // "----------bug".replace(/^-/g,"") == "bug" 1018 | bq = bq.replace(/^[ \t]*>[ \t]?/gm, "~0"); // trim one level of quoting 1019 | // attacklab: clean up hack 1020 | bq = bq.replace(/~0/g, ""); 1021 | 1022 | bq = bq.replace(/^[ \t]+$/gm, ""); // trim whitespace-only lines 1023 | bq = _RunBlockGamut(bq); // recurse 1024 | bq = bq.replace(/(^|\n)/g, "$1 "); 1025 | // These leading spaces screw with
     content, so we need to fix that:
    1026 | 			bq = bq.replace(/(\s*
    [^\r]+?<\/pre>)/gm, function (wholeMatch, m1) {
    1027 | 				var pre = m1;
    1028 | 				// attacklab: hack around Konqueror 3.5.4 bug:
    1029 | 				pre = pre.replace(/^ {2}/mg, "~0");
    1030 | 				pre = pre.replace(/~0/g, "");
    1031 | 				return pre;
    1032 | 			});
    1033 | 
    1034 | 			return hashBlock("
    \n" + bq + "\n
    "); 1035 | }); 1036 | return text; 1037 | }; 1038 | 1039 | 1040 | var _FormParagraphs = function (text) { 1041 | // 1042 | // Params: 1043 | // $text - string to process with html

    tags 1044 | // 1045 | // Strip leading and trailing lines: 1046 | text = text.replace(/^\n+/g, ""); 1047 | text = text.replace(/\n+$/g, ""); 1048 | 1049 | var i; 1050 | var grafs = text.split(/\n{2,}/g); 1051 | var grafsOut = []; 1052 | 1053 | // 1054 | // Wrap

    tags. 1055 | // 1056 | var end = grafs.length; 1057 | for (i = 0; i < end; i++) { 1058 | var str = grafs[i]; 1059 | var p_tag = '

    '; 1060 | 1061 | // if this is an HTML marker, copy it 1062 | if (str.search(/~K(\d+)K/g) >= 0) { 1063 | grafsOut.push(str); 1064 | } 1065 | else if (str.search(/\S/) >= 0) { 1066 | str = _RunSpanGamut(str); 1067 | 1068 | if (str.substr(0,2)==='->') { 1069 | if (str.substr(-5)==='<-') { 1070 | p_tag = '

    '; 1071 | str = str.slice(2,-5); 1072 | } else { 1073 | p_tag = '

    '; 1074 | str = str.substring(2); 1075 | } 1076 | } 1077 | 1078 | str = str.replace(/\n/g, "
    "); // ** GFM ** 1079 | str = str.replace(/^([ \t]*)/g, p_tag); 1080 | str += "

    "; 1081 | grafsOut.push(str); 1082 | } 1083 | 1084 | } 1085 | 1086 | // 1087 | // Unhashify HTML blocks 1088 | // 1089 | end = grafsOut.length; 1090 | for (i = 0; i < end; i++) { 1091 | // if this is a marker for an html block... 1092 | while (grafsOut[i].search(/~K(\d+)K/) >= 0) { 1093 | var blockText = g_html_blocks[RegExp.$1]; 1094 | blockText = blockText.replace(/\$/g, "$$$$"); // Escape any dollar signs 1095 | grafsOut[i] = grafsOut[i].replace(/~K\d+K/, blockText); 1096 | } 1097 | } 1098 | 1099 | return grafsOut.join("\n\n"); 1100 | }; 1101 | 1102 | 1103 | var _EncodeAmpsAndAngles = function (text) { 1104 | // Smart processing for ampersands and angle brackets that need to be encoded. 1105 | // Ampersand-encoding based entirely on Nat Irons's Amputator MT plugin: 1106 | // http://bumppo.net/projects/amputator/ 1107 | text = text.replace(/&(?!#?[xX]?(?:[0-9a-fA-F]+|\w+);)/g, "&"); 1108 | 1109 | // Encode naked <'s 1110 | text = text.replace(/<(?![a-z\/?\$!])/gi, "<"); 1111 | 1112 | // Encode "smart" quotes 1113 | text = text. 1114 | replace( /\u2026/g , '…'). 1115 | replace( /\u00AB/g , '«' ). 1116 | replace( /\u00BB/g , '»' ). 1117 | replace( /\u201C/g , '“' ). 1118 | replace( /\u201D/g , '”' ). 1119 | replace( /\u2018/g , '‘' ). 1120 | replace( /\u2019/g , '’' ). 1121 | replace( /\u2014/g , '—' ). 1122 | replace( /\u2013/g , '–' ). 1123 | replace( /\u2022/g , '•' ). 1124 | replace( /\u2122/g , '™' ). 1125 | replace( /\u00A9/g , '©' ). 1126 | replace( /\u00AE/g , '®' ); 1127 | 1128 | return text; 1129 | }; 1130 | 1131 | var _ConvertExtraSpecialCharacters = function (text) { 1132 | // Processing to change various special character combinations into 1133 | // common real characters. 1134 | 1135 | text = text. 1136 | replace( /\.\.\./g , '…'). 1137 | replace( /\(c\)/g , '©'). 1138 | replace( /\(r\)/g , '®'). 1139 | replace( /\(tm\)/g , '™'). 1140 | replace( /\-\-/g, '—'); 1141 | 1142 | return text; 1143 | }; 1144 | 1145 | 1146 | var _EncodeBackslashEscapes = function (text) { 1147 | // 1148 | // Parameter: String. 1149 | // Returns: The string, with after processing the following backslash 1150 | // escape sequences. 1151 | // 1152 | // attacklab: The polite way to do this is with the new 1153 | // escapeCharacters() function: 1154 | // 1155 | // text = escapeCharacters(text,"\\",true); 1156 | // text = escapeCharacters(text,"`*_{}[]()>#+-.!",true); 1157 | // 1158 | // ...but we're sidestepping its use of the (slow) RegExp constructor 1159 | // as an optimization for Firefox. This function gets called a LOT. 1160 | text = text.replace(/\\(\\)/g, escapeCharacters_callback); 1161 | text = text.replace(/\\([`*_{}\[\]()>#+-.!])/g, escapeCharacters_callback); 1162 | return text; 1163 | }; 1164 | 1165 | 1166 | var _DoAutoLinks = function (text) { 1167 | 1168 | text = text.replace(/(?:")<((https?|ftp|dict):[^'">\s]+)>/gi, "
    $1"); 1169 | 1170 | // Email addresses: 1171 | /* 1172 | text = text.replace(/ 1173 | < 1174 | (?:mailto:)? 1175 | ( 1176 | [-.\w]+ 1177 | \@ 1178 | [-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+ 1179 | ) 1180 | > 1181 | /gi, _DoAutoLinks_callback()); 1182 | */ 1183 | text = text.replace(/<(?:mailto:)?([\-.\w]+\@[\-a-z0-9]+(\.[\-a-z0-9]+)*\.[a-z]+)>/gi, function (wholeMatch, m1) { 1184 | return _EncodeEmailAddress(_UnescapeSpecialChars(m1)); 1185 | }); 1186 | 1187 | return text; 1188 | }; 1189 | 1190 | 1191 | var _EncodeEmailAddress = function (addr) { 1192 | // 1193 | // Input: an email address, e.g. "foo@example.com" 1194 | // 1195 | // Output: the email address as a mailto link, with each character 1196 | // of the address encoded as either a decimal or hex entity, in 1197 | // the hopes of foiling most address harvesting spam bots. E.g.: 1198 | // 1199 | // foo 1201 | // @example.com 1202 | // 1203 | // Based on a filter by Matthew Wickline, posted to the BBEdit-Talk 1204 | // mailing list: 1205 | // 1206 | // attacklab: why can't javascript speak hex? 1207 | 1208 | 1209 | function char2hex(ch) { 1210 | var hexDigits = '0123456789ABCDEF'; 1211 | var dec = ch.charCodeAt(0); 1212 | return (hexDigits.charAt(dec >> 4) + hexDigits.charAt(dec & 15)); 1213 | } 1214 | 1215 | var encode = [ 1216 | function (ch) { 1217 | return "&#" + ch.charCodeAt(0) + ";";}, 1218 | function (ch) { 1219 | return "&#x" + char2hex(ch) + ";";}, 1220 | function (ch) { 1221 | return ch;} 1222 | ]; 1223 | 1224 | addr = "mailto:" + addr; 1225 | 1226 | addr = addr.replace(/./g, function (ch) { 1227 | if (ch == "@") { 1228 | // this *must* be encoded. I insist. 1229 | ch = encode[Math.floor(Math.random() * 2)](ch); 1230 | } else if (ch != ":") { 1231 | // leave ':' alone (to spot mailto: later) 1232 | var r = Math.random(); 1233 | // roughly 10% raw, 45% hex, 45% dec 1234 | ch = ( 1235 | r > 0.9 ? encode[2](ch) : r > 0.45 ? encode[1](ch) : encode[0](ch)); 1236 | } 1237 | return ch; 1238 | }); 1239 | 1240 | addr = "" + addr + ""; 1241 | addr = addr.replace(/">.+:/g, "\">"); // strip the mailto: from the visible part 1242 | return addr; 1243 | }; 1244 | 1245 | 1246 | var _UnescapeSpecialChars = function (text) { 1247 | // 1248 | // Swap back in all the special characters we've hidden. 1249 | // 1250 | text = text.replace(/~E(\d+)E/g, function (wholeMatch, m1) { 1251 | var charCodeToReplace = parseInt(m1,10); 1252 | return String.fromCharCode(charCodeToReplace); 1253 | }); 1254 | return text; 1255 | }; 1256 | 1257 | 1258 | var _Outdent = function (text) { 1259 | // 1260 | // Remove one level of line-leading tabs or spaces 1261 | // 1262 | // attacklab: hack around Konqueror 3.5.4 bug: 1263 | // "----------bug".replace(/^-/g,"") == "bug" 1264 | text = text.replace(/^(\t|[ ]{1,4})/gm, "~0"); // attacklab: g_tab_width 1265 | // attacklab: clean up hack 1266 | text = text.replace(/~0/g, ""); 1267 | 1268 | return text; 1269 | }; 1270 | 1271 | var _Detab = function (text) { 1272 | // attacklab: Detab's completely rewritten for speed. 1273 | // In perl we could fix it by anchoring the regexp with \G. 1274 | // In javascript we're less fortunate. 1275 | // expand first n-1 tabs 1276 | text = text.replace(/\t(?=\t)/g, " "); // attacklab: g_tab_width 1277 | // replace the nth with two sentinels 1278 | text = text.replace(/\t/g, "~A~B"); 1279 | 1280 | // use the sentinel to anchor our regex so it doesn't explode 1281 | text = text.replace(/~B(.+?)~A/g, function (wholeMatch, m1, m2) { 1282 | var leadingText = m1; 1283 | var numSpaces = 4 - leadingText.length % 4; // attacklab: g_tab_width 1284 | // there *must* be a better way to do this: 1285 | for (var i = 0; i < numSpaces; i++) leadingText += " "; 1286 | 1287 | return leadingText; 1288 | }); 1289 | 1290 | // clean up sentinels 1291 | text = text.replace(/~A/g, " "); // attacklab: g_tab_width 1292 | text = text.replace(/~B/g, ""); 1293 | 1294 | return text; 1295 | }; 1296 | 1297 | 1298 | // 1299 | // attacklab: Utility functions 1300 | // 1301 | 1302 | var escapeCharacters = function (text, charsToEscape, afterBackslash) { 1303 | // First we have to escape the escape characters so that 1304 | // we can build a character class out of them 1305 | var regexString = "([" + charsToEscape.replace(/([\[\]\\])/g, "\\$1") + "])"; 1306 | 1307 | if (afterBackslash) { 1308 | regexString = "\\\\" + regexString; 1309 | } 1310 | 1311 | var regex = new RegExp(regexString, "g"); 1312 | text = text.replace(regex, escapeCharacters_callback); 1313 | 1314 | return text; 1315 | }; 1316 | 1317 | 1318 | var escapeCharacters_callback = function (wholeMatch, m1) { 1319 | var charCodeToEscape = m1.charCodeAt(0); 1320 | return "~E" + charCodeToEscape + "E"; 1321 | }; 1322 | 1323 | }; // end of Attacklab.showdown.converter 1324 | -------------------------------------------------------------------------------- /wmd.combined.min.js: -------------------------------------------------------------------------------- 1 | ;(function(){WMDEditor=function(options){this.options=WMDEditor.util.extend({},WMDEditor.defaults,options||{});wmdBase(this,this.options);this.startEditor();};window.WMDEditor=WMDEditor;WMDEditor.defaults={version:2.1,output_format:"markdown",lineLength:40,button_bar:"wmd-button-bar",preview:"wmd-preview",output:"wmd-output",input:"wmd-input",imageDialogText:"

    Enter the image URL.

    You can also add a title, which will be displayed as a tool tip.

    Example:
    http://i.imgur.com/1cZl4.jpg

    ",linkDialogText:"

    Enter the web address.

    You can also add a title, which will be displayed as a tool tip.

    Example:
    http://www.google.com/

    ",imageDefaultText:"http://",linkDefaultText:"http://",imageDirectory:"images/",helpLink:"/wmd/markdownhelp.html",helpHoverTitle:"Markdown Syntax",helpTarget:"_blank",previewPollInterval:500,pastePollInterval:100,buttons:"bold italic link blockquote code image ol ul heading hr undo redo help",autoFormatting:{list:true,quote:true,code:true,},modifierKeys:{bold:"b",italic:"i",link:"l",quote:"q",code:"k",image:"g",orderedList:"o",unorderedList:"u",heading:"h",horizontalRule:"r",redo:"y",undo:"z"},tagFilter:{enabled:false,allowedTags:/^(<\/?(b|blockquote|code|del|dd|dl|dt|em|h1|h2|h3|i|kbd|li|ol|p|pre|s|sup|sub|strong|strike|ul)>|<(br|hr)\s?\/?>)$/i,patternLink:/^(]+")?\s?>|<\/a>)$/i,patternImage:/^(]*")?(\stitle="[^"<>]*")?\s?\/?>)$/i}};WMDEditor.prototype={getPanels:function(){return{buttonBar:(typeof this.options.button_bar=='string')?document.getElementById(this.options.button_bar):this.options.button_bar,preview:(typeof this.options.preview=='string')?document.getElementById(this.options.preview):this.options.preview,output:(typeof this.options.output=='string')?document.getElementById(this.options.output):this.options.output,input:(typeof this.options.input=='string')?document.getElementById(this.options.input):this.options.input};},startEditor:function(){this.panels=this.getPanels();this.previewMgr=new PreviewManager(this);edit=new this.editor(this.previewMgr.refresh);this.previewMgr.refresh(true);}};var util={isVisible:function(elem){return elem.offsetWidth>0||elem.offsetHeight>0;},addEvent:function(elem,event,listener){if(elem.attachEvent){elem.attachEvent("on"+event,listener);} 2 | else{elem.addEventListener(event,listener,false);}},removeEvent:function(elem,event,listener){if(elem.detachEvent){elem.detachEvent("on"+event,listener);} 3 | else{elem.removeEventListener(event,listener,false);}},fixEolChars:function(text){text=text.replace(/\r\n/g,"\n");text=text.replace(/\r/g,"\n");return text;},extendRegExp:function(regex,pre,post){if(pre===null||pre===undefined){pre="";} 4 | if(post===null||post===undefined){post="";} 5 | var pattern=regex.toString();var flags="";var result=pattern.match(/\/([gim]*)$/);if(result===null){flags=result[0];} 6 | else{flags="";} 7 | pattern=pattern.replace(/(^\/|\/[gim]*$)/g,"");pattern=pre+pattern+post;return new RegExp(pattern,flags);},createImage:function(img){var imgPath=imageDirectory+img;var elem=document.createElement("img");elem.className="wmd-button";elem.src=imgPath;return elem;},prompt:function(text,defaultInputText,makeLinkMarkdown,promptType){var dialog;var background;var input;var titleInput;var newWinCheckbox;if(defaultInputText===undefined){defaultInputText="";} 8 | var checkEscape=function(key){var code=(key.charCode||key.keyCode);if(code===27){close(true);}};var close=function(isCancel){util.removeEvent(document.body,"keydown",checkEscape);var text=input.value+(titleInput.value?' "'+titleInput.value+'"':'');if(isCancel){text=null;} 9 | else{text=text.replace('http://http://','http://');text=text.replace('http://https://','https://');text=text.replace('http://ftp://','ftp://');if(promptType=='link'&&newWinCheckbox.checked)text='!'+text;} 10 | dialog.parentNode.removeChild(dialog);background.parentNode.removeChild(background);makeLinkMarkdown(text);return false;};var createBackground=function(){background=document.createElement("div");background.className="wmd-prompt-background";style=background.style;style.position="absolute";style.top="0";style.zIndex="10000";if(browser.isKonqueror){style.backgroundColor="transparent";} 11 | else if(browser.isIE){style.filter="alpha(opacity=50)";} 12 | else{style.opacity="0.5";} 13 | var pageSize=position.getPageSize();style.height=pageSize[1]+"px";if(browser.isIE){style.left=document.documentElement.scrollLeft;style.width=document.documentElement.clientWidth;} 14 | else{style.left="0";style.width="100%";} 15 | document.body.appendChild(background);};var createDialog=function(){dialog=document.createElement("div");dialog.className="wmd-prompt-dialog";dialog.style.padding="10px;";dialog.style.position="fixed";dialog.style.width="400px";dialog.style.zIndex="10001";var question=document.createElement("div");question.innerHTML=text;question.style.padding="5px";dialog.appendChild(question);var form=document.createElement("form");form.onsubmit=function(){return close(false);};var style=form.style;style.padding="0";style.margin="0";style.cssFloat="left";style.width="100%";style.textAlign="center";style.position="relative";dialog.appendChild(form);var label=document.createElement("label");style=label.style;style.display="block";style.width="80%";style.marginLeft=style.marginRight="auto";style.textAlign="left";form.appendChild(label);label.appendChild(document.createTextNode(promptType+" URL:"));input=document.createElement("input");input.type="text";input.value=defaultInputText;style=input.style;style.display="block";style.width="100%";style.marginLeft=style.marginRight="auto";label.appendChild(input);label=document.createElement("label");style=label.style;style.display="block";style.width="80%";style.marginLeft=style.marginRight="auto";style.textAlign="left";form.appendChild(label);label.appendChild(document.createTextNode(promptType+" Title (Hover Text):"));titleInput=document.createElement("input");titleInput.type="text";style=titleInput.style;style.display="block";style.width="100%";style.marginLeft=style.marginRight="auto";label.appendChild(titleInput);if(promptType=='link'){label=document.createElement("label");style=label.style;style.display="block";style.textAlign="center";form.appendChild(label);newWinCheckbox=document.createElement("input");newWinCheckbox.type='checkbox';newWinCheckbox.value='!';label.appendChild(newWinCheckbox);label.appendChild(document.createTextNode(" Have this link open in a new window"));} 16 | var okButton=document.createElement("input");okButton.type="button";okButton.onclick=function(){return close(false);};okButton.value="OK";style=okButton.style;style.margin="10px";style.display="inline";style.width="7em";var cancelButton=document.createElement("input");cancelButton.type="button";cancelButton.onclick=function(){return close(true);};cancelButton.value="Cancel";style=cancelButton.style;style.margin="10px";style.display="inline";style.width="7em";if(/mac/.test(nav.platform.toLowerCase())){form.appendChild(cancelButton);form.appendChild(okButton);} 17 | else{form.appendChild(okButton);form.appendChild(cancelButton);} 18 | util.addEvent(document.body,"keydown",checkEscape);dialog.style.top="50%";dialog.style.left="50%";dialog.style.display="block";if(browser.isIE_5or6){dialog.style.position="absolute";dialog.style.top=document.documentElement.scrollTop+200+"px";dialog.style.left="50%";} 19 | document.body.appendChild(dialog);dialog.style.marginTop=-(position.getHeight(dialog)/2)+"px";dialog.style.marginLeft=-(position.getWidth(dialog)/2)+"px";};createBackground();window.setTimeout(function(){createDialog();var defTextLen=defaultInputText.length;if(input.selectionStart!==undefined){input.selectionStart=0;input.selectionEnd=defTextLen;} 20 | else if(input.createTextRange){var range=input.createTextRange();range.collapse(false);range.moveStart("character",-defTextLen);range.moveEnd("character",defTextLen);range.select();} 21 | input.focus();},0);},extend:function(){function _update(a,b){for(var k in b)if(b.hasOwnProperty(k)){if(typeof a[k]==='object'&&typeof b[k]==='object')_update(a[k],b[k]);else a[k]=b[k];} 22 | return a;} 23 | var d={};for(var i=0;idocument.body.offsetHeight){scrollWidth=document.body.scrollWidth;scrollHeight=document.body.scrollHeight;} 27 | else{scrollWidth=document.body.offsetWidth;scrollHeight=document.body.offsetHeight;} 28 | if(self.innerHeight){innerWidth=self.innerWidth;innerHeight=self.innerHeight;} 29 | else if(document.documentElement&&document.documentElement.clientHeight){innerWidth=document.documentElement.clientWidth;innerHeight=document.documentElement.clientHeight;} 30 | else if(document.body){innerWidth=document.body.clientWidth;innerHeight=document.body.clientHeight;} 31 | var maxWidth=Math.max(scrollWidth,innerWidth);var maxHeight=Math.max(scrollHeight,innerHeight);return[maxWidth,maxHeight,innerWidth,innerHeight];}};var TextareaState=function(textarea,wmd){var stateObj=this;var inputArea=textarea;this.init=function(){if(!util.isVisible(inputArea)){return;} 32 | this.setInputAreaSelectionStartEnd();this.scrollTop=inputArea.scrollTop;if(!this.text&&inputArea.selectionStart||inputArea.selectionStart===0){this.text=inputArea.value;}};this.setInputAreaSelection=function(){if(!util.isVisible(inputArea)){return;} 33 | if(inputArea.selectionStart!==undefined&&!browser.isOpera){inputArea.focus();inputArea.selectionStart=stateObj.start;inputArea.selectionEnd=stateObj.end;inputArea.scrollTop=stateObj.scrollTop;} 34 | else if(document.selection){if(typeof(document.activeElement)!="unknown"&&document.activeElement&&document.activeElement!==inputArea){return;} 35 | inputArea.focus();var range=inputArea.createTextRange();range.moveStart("character",-inputArea.value.length);range.moveEnd("character",-inputArea.value.length);range.moveEnd("character",stateObj.end);range.moveStart("character",stateObj.start);range.select();}};this.setInputAreaSelectionStartEnd=function(){if(inputArea.selectionStart||inputArea.selectionStart===0){stateObj.start=inputArea.selectionStart;stateObj.end=inputArea.selectionEnd;} 36 | else if(document.selection){stateObj.text=util.fixEolChars(inputArea.value);var range;if(wmd.ieRetardedClick&&wmd.ieCachedRange){range=wmd.ieCachedRange;wmd.ieRetardedClick=false;} 37 | else{range=document.selection.createRange();} 38 | var fixedRange=util.fixEolChars(range.text);var marker="\x07";var markedRange=marker+fixedRange+marker;range.text=markedRange;var inputText=util.fixEolChars(inputArea.value);range.moveStart("character",-markedRange.length);range.text=fixedRange;stateObj.start=inputText.indexOf(marker);stateObj.end=inputText.lastIndexOf(marker)-marker.length;var len=stateObj.text.length-util.fixEolChars(inputArea.value).length;if(len){range.moveStart("character",-fixedRange.length);while(len--){fixedRange+="\n";stateObj.end+=1;} 39 | range.text=fixedRange;} 40 | this.setInputAreaSelection();}};this.restore=function(){if(stateObj.text!=undefined&&stateObj.text!=inputArea.value){inputArea.value=stateObj.text;} 41 | this.setInputAreaSelection();inputArea.scrollTop=stateObj.scrollTop;};this.getChunks=function(){var chunk=new Chunks();chunk.before=util.fixEolChars(stateObj.text.substring(0,stateObj.start));chunk.startTag="";chunk.selection=util.fixEolChars(stateObj.text.substring(stateObj.start,stateObj.end));chunk.endTag="";chunk.after=util.fixEolChars(stateObj.text.substring(stateObj.end));chunk.scrollTop=stateObj.scrollTop;return chunk;};this.setChunks=function(chunk){chunk.before=chunk.before+chunk.startTag;chunk.after=chunk.endTag+chunk.after;if(browser.isOpera){chunk.before=chunk.before.replace(/\n/g,"\r\n");chunk.selection=chunk.selection.replace(/\n/g,"\r\n");chunk.after=chunk.after.replace(/\n/g,"\r\n");} 42 | this.start=chunk.before.length;this.end=chunk.before.length+chunk.selection.length;this.text=chunk.before+chunk.selection+chunk.after;this.scrollTop=chunk.scrollTop;};this.init();};var Chunks=function(){};Chunks.prototype.findTags=function(startRegex,endRegex){var chunkObj=this;var regex;if(startRegex){regex=util.extendRegExp(startRegex,"","$");this.before=this.before.replace(regex,function(match){chunkObj.startTag=chunkObj.startTag+match;return"";});regex=util.extendRegExp(startRegex,"^","");this.selection=this.selection.replace(regex,function(match){chunkObj.startTag=chunkObj.startTag+match;return"";});} 43 | if(endRegex){regex=util.extendRegExp(endRegex,"","$");this.selection=this.selection.replace(regex,function(match){chunkObj.endTag=match+chunkObj.endTag;return"";});regex=util.extendRegExp(endRegex,"^","");this.after=this.after.replace(regex,function(match){chunkObj.endTag=match+chunkObj.endTag;return"";});}};Chunks.prototype.trimWhitespace=function(remove){this.selection=this.selection.replace(/^(\s*)/,"");if(!remove){this.before+=re.$1;} 44 | this.selection=this.selection.replace(/(\s*)$/,"");if(!remove){this.after=re.$1+this.after;}};Chunks.prototype.addBlankLines=function(nLinesBefore,nLinesAfter,findExtraNewlines){if(nLinesBefore===undefined){nLinesBefore=1;} 45 | if(nLinesAfter===undefined){nLinesAfter=1;} 46 | nLinesBefore++;nLinesAfter++;var regexText;var replacementText;var match=/(^\n*)/.exec(this.selection);this.selection=this.selection.replace(/(^\n*)/,"");this.startTag=this.startTag+(match?match[1]:"");match=/(\n*$)/.exec(this.selection);this.selection=this.selection.replace(/(\n*$)/,"");this.endTag=this.endTag+(match?match[1]:"");match=/(^\n*)/.exec(this.startTag);this.startTag=this.startTag.replace(/(^\n*)/,"");this.before=this.before+(match?match[1]:"");match=/(\n*$)/.exec(this.endTag);this.endTag=this.endTag.replace(/(\n*$)/,"");this.after=this.after+(match?match[1]:"");if(this.before){regexText=replacementText="";while(nLinesBefore--){regexText+="\\n?";replacementText+="\n";} 47 | if(findExtraNewlines){regexText="\\n*";} 48 | this.before=this.before.replace(new re(regexText+"$",""),replacementText);} 49 | if(this.after){regexText=replacementText="";while(nLinesAfter--){regexText+="\\n?";replacementText+="\n";} 50 | if(findExtraNewlines){regexText="\\n*";} 51 | this.after=this.after.replace(new re(regexText,""),replacementText);}};var InputPoller=function(textarea,callback,interval){var pollerObj=this;var inputArea=textarea;var lastStart;var lastEnd;var markdown;var killHandle;this.tick=function(){if(!util.isVisible(inputArea)){return;} 52 | if(inputArea.selectionStart||inputArea.selectionStart===0){var start=inputArea.selectionStart;var end=inputArea.selectionEnd;if(start!=lastStart||end!=lastEnd){lastStart=start;lastEnd=end;if(markdown!=inputArea.value){markdown=inputArea.value;return true;}}} 53 | return false;};var doTickCallback=function(){if(!util.isVisible(inputArea)){return;} 54 | if(pollerObj.tick()){callback();}};var assignInterval=function(){killHandle=window.setInterval(doTickCallback,interval);};this.destroy=function(){window.clearInterval(killHandle);};assignInterval();};var PreviewManager=function(wmd){var managerObj=this;var converter;var poller;var timeout;var elapsedTime;var oldInputText;var htmlOut;var maxDelay=3000;var startType="delayed";var setupEvents=function(inputElem,listener){util.addEvent(inputElem,"input",listener);inputElem.onpaste=listener;inputElem.ondrop=listener;util.addEvent(inputElem,"keypress",listener);util.addEvent(inputElem,"keydown",listener);poller=new InputPoller(wmd.panels.input,listener,wmd.options.previewPollInterval);};var getDocScrollTop=function(){var result=0;if(window.innerHeight){result=window.pageYOffset;} 55 | else if(document.documentElement&&document.documentElement.scrollTop){result=document.documentElement.scrollTop;} 56 | else if(document.body){result=document.body.scrollTop;} 57 | return result;};var makePreviewHtml=function(){if(!wmd.panels.preview&&!wmd.panels.output){return;} 58 | var text=wmd.panels.input.value;if(text&&text==oldInputText){return;} 59 | else{oldInputText=text;} 60 | var prevTime=new Date().getTime();if(!converter&&wmd.showdown){converter=new wmd.showdown.converter();} 61 | if(converter){text=converter.makeHtml(text);} 62 | var currTime=new Date().getTime();elapsedTime=currTime-prevTime;pushPreviewHtml(text);htmlOut=text;};var applyTimeout=function(){if(timeout){window.clearTimeout(timeout);timeout=undefined;} 63 | if(startType!=="manual"){var delay=0;if(startType==="delayed"){delay=elapsedTime;} 64 | if(delay>maxDelay){delay=maxDelay;} 65 | timeout=window.setTimeout(makePreviewHtml,delay);}};var getScaleFactor=function(panel){if(panel.scrollHeight<=panel.clientHeight){return 1;} 66 | return panel.scrollTop/(panel.scrollHeight-panel.clientHeight);};var setPanelScrollTops=function(){if(wmd.panels.preview){wmd.panels.preview.scrollTop=(wmd.panels.preview.scrollHeight-wmd.panels.preview.clientHeight)*getScaleFactor(wmd.panels.preview);;} 67 | if(wmd.panels.output){wmd.panels.output.scrollTop=(wmd.panels.output.scrollHeight-wmd.panels.output.clientHeight)*getScaleFactor(wmd.panels.output);;}};this.refresh=function(requiresRefresh){if(requiresRefresh){oldInputText="";makePreviewHtml();} 68 | else{applyTimeout();}};this.processingTime=function(){return elapsedTime;};this.output=function(){return htmlOut;};this.setUpdateMode=function(mode){startType=mode;managerObj.refresh();};var isFirstTimeFilled=true;var pushPreviewHtml=function(text){var emptyTop=position.getTop(wmd.panels.input)-getDocScrollTop();if(wmd.panels.output){if(wmd.panels.output.value!==undefined){wmd.panels.output.value=text;} 69 | else{var newText=text.replace(/&/g,"&");newText=newText.replace(/"+newText+"
    ";}} 70 | if(wmd.panels.preview){if(wmd.options.tagFilter.enabled){text=text.replace(/<[^<>]*>?/gi,function(tag){return(tag.match(wmd.options.tagFilter.allowedTags)||tag.match(wmd.options.tagFilter.patternLink)||tag.match(wmd.options.tagFilter.patternImage))?tag:"";});} 71 | wmd.panels.preview.innerHTML=text;} 72 | setPanelScrollTops();if(isFirstTimeFilled){isFirstTimeFilled=false;return;} 73 | var fullTop=position.getTop(wmd.panels.input)-getDocScrollTop();if(browser.isIE){window.setTimeout(function(){window.scrollBy(0,fullTop-emptyTop);},0);} 74 | else{window.scrollBy(0,fullTop-emptyTop);}};var init=function(){setupEvents(wmd.panels.input,applyTimeout);makePreviewHtml();if(wmd.panels.preview){wmd.panels.preview.scrollTop=0;} 75 | if(wmd.panels.output){wmd.panels.output.scrollTop=0;}};this.destroy=function(){if(poller){poller.destroy();}};init();};var UndoManager=function(wmd,textarea,pastePollInterval,callback){var undoObj=this;var undoStack=[];var stackPtr=0;var mode="none";var lastState;var poller;var timer;var inputStateObj;var setMode=function(newMode,noSave){if(mode!=newMode){mode=newMode;if(!noSave){saveState();}} 76 | if(!browser.isIE||mode!="moving"){timer=window.setTimeout(refreshState,1);} 77 | else{inputStateObj=null;}};var refreshState=function(){inputStateObj=new TextareaState(textarea,wmd);poller.tick();timer=undefined;};this.setCommandMode=function(){mode="command";saveState();timer=window.setTimeout(refreshState,0);};this.canUndo=function(){return stackPtr>1;};this.canRedo=function(){if(undoStack[stackPtr+1]){return true;} 78 | return false;};this.undo=function(){if(undoObj.canUndo()){if(lastState){lastState.restore();lastState=null;} 79 | else{undoStack[stackPtr]=new TextareaState(textarea,wmd);undoStack[--stackPtr].restore();if(callback){callback();}}} 80 | mode="none";textarea.focus();refreshState();};this.redo=function(){if(undoObj.canRedo()){undoStack[++stackPtr].restore();if(callback){callback();}} 81 | mode="none";textarea.focus();refreshState();};var saveState=function(){var currState=inputStateObj||new TextareaState(textarea,wmd);if(!currState){return false;} 82 | if(mode=="moving"){if(!lastState){lastState=currState;} 83 | return;} 84 | if(lastState){if(undoStack[stackPtr-1].text!=lastState.text){undoStack[stackPtr++]=lastState;} 85 | lastState=null;} 86 | undoStack[stackPtr++]=currState;undoStack[stackPtr+1]=null;if(callback){callback();}};var handleCtrlYZ=function(event){var handled=false;if(event.ctrlKey||event.metaKey){var keyCode=event.charCode||event.keyCode;var keyCodeChar=String.fromCharCode(keyCode);switch(keyCodeChar){case"y":undoObj.redo();handled=true;break;case"z":if(!event.shiftKey){undoObj.undo();} 87 | else{undoObj.redo();} 88 | handled=true;break;}} 89 | if(handled){if(event.preventDefault){event.preventDefault();} 90 | if(window.event){window.event.returnValue=false;} 91 | return;}};var handleModeChange=function(event){if(!event.ctrlKey&&!event.metaKey){var keyCode=event.keyCode;if((keyCode>=33&&keyCode<=40)||(keyCode>=63232&&keyCode<=63235)){setMode("moving");} 92 | else if(keyCode==8||keyCode==46||keyCode==127){setMode("deleting");} 93 | else if(keyCode==13){setMode("newlines");} 94 | else if(keyCode==27){setMode("escape");} 95 | else if((keyCode<16||keyCode>20)&&keyCode!=91){setMode("typing");}}};var setEventHandlers=function(){util.addEvent(textarea,"keypress",function(event){if((event.ctrlKey||event.metaKey)&&(event.keyCode==89||event.keyCode==90)){event.preventDefault();}});var handlePaste=function(){if(browser.isIE||(inputStateObj&&inputStateObj.text!=textarea.value)){if(timer==undefined){mode="paste";saveState();refreshState();}}};poller=new InputPoller(textarea,handlePaste,pastePollInterval);util.addEvent(textarea,"keydown",handleCtrlYZ);util.addEvent(textarea,"keydown",handleModeChange);util.addEvent(textarea,"mousedown",function(){setMode("moving");});textarea.onpaste=handlePaste;textarea.ondrop=handlePaste;};var init=function(){setEventHandlers();refreshState();saveState();};this.destroy=function(){if(poller){poller.destroy();}};init();};WMDEditor.util=util;WMDEditor.position=position;WMDEditor.TextareaState=TextareaState;WMDEditor.InputPoller=InputPoller;WMDEditor.PreviewManager=PreviewManager;WMDEditor.UndoManager=UndoManager;var doc=window.document;var re=window.RegExp;var nav=window.navigator;function get_browser(){var b={};b.isIE=/msie/.test(nav.userAgent.toLowerCase());b.isIE_5or6=/msie 6/.test(nav.userAgent.toLowerCase())||/msie 5/.test(nav.userAgent.toLowerCase());b.isIE_7plus=b.isIE&&!b.isIE_5or6;b.isOpera=/opera/.test(nav.userAgent.toLowerCase());b.isKonqueror=/konqueror/.test(nav.userAgent.toLowerCase());return b;} 96 | var browser=get_browser();var wmdBase=function(wmd,wmd_options){wmd.Command={};wmd.Global={};wmd.buttons={};wmd.showdown=window.Showdown;var util=WMDEditor.util;var position=WMDEditor.position;var command=wmd.Command;wmd.ieCachedRange=null;wmd.ieRetardedClick=false;wmd.editor=function(previewRefreshCallback){if(!previewRefreshCallback){previewRefreshCallback=function(){};} 97 | var inputBox=wmd.panels.input;var offsetHeight=0;var editObj=this;var mainDiv;var mainSpan;var div;var creationHandle;var undoMgr;var doClick=function(button){inputBox.focus();if(button.textOp){if(undoMgr){undoMgr.setCommandMode();} 98 | var state=new TextareaState(wmd.panels.input,wmd);if(!state){return;} 99 | var chunks=state.getChunks();var fixupInputArea=function(){inputBox.focus();if(chunks){state.setChunks(chunks);} 100 | state.restore();previewRefreshCallback();};var useDefaultText=true;var noCleanup=button.textOp(chunks,fixupInputArea,useDefaultText);if(!noCleanup){fixupInputArea();}} 101 | if(button.execute){button.execute(editObj);}};var setUndoRedoButtonStates=function(){if(undoMgr){if(wmd.buttons["wmd-undo-button"])setupButton(wmd.buttons["wmd-undo-button"],undoMgr.canUndo());if(wmd.buttons["wmd-redo-button"])setupButton(wmd.buttons["wmd-redo-button"],undoMgr.canRedo());}};var setupButton=function(button,isEnabled){if(isEnabled){button.className=button.className.replace(new RegExp("(^|\\s+)disabled(\\s+|$)"),' ');if(browser.isIE){button.onmousedown=function(){wmd.ieRetardedClick=true;wmd.ieCachedRange=document.selection.createRange();};} 102 | if(!button.isHelp){button.onclick=function(){if(this.onmouseout){this.onmouseout();} 103 | doClick(this);return false;};}} 104 | else{button.className+=(button.className?' ':'')+'disabled';button.onmouseover=button.onmouseout=button.onclick=function(){};}};var makeSpritedButtonRow=function(){var buttonBar=(typeof wmd_options.button_bar=='string')?document.getElementById(wmd_options.button_bar||"wmd-button-bar"):wmd_options.button_bar;var normalYShift="0px";var disabledYShift="-20px";var highlightYShift="-40px";var buttonRow=document.createElement("ul");buttonRow.className="wmd-button-row";buttonRow=buttonBar.appendChild(buttonRow);var xoffset=0;function createButton(name,title,textOp){var button=document.createElement("li");wmd.buttons[name]=button;button.className="wmd-button "+name;button.XShift=xoffset+"px";xoffset-=20;if(title)button.title=title;if(textOp)button.textOp=textOp;return button;} 105 | function addButton(name,title,textOp){var button=createButton(name,title,textOp);setupButton(button,true);buttonRow.appendChild(button);return button;} 106 | function addSpacer(){var spacer=document.createElement("li");spacer.className="wmd-spacer";buttonRow.appendChild(spacer);return spacer;} 107 | var modifierKey=(navigator.appVersion.indexOf("Mac")!=-1)?"Cmd":"Ctrl";var buttonlist=wmd_options.buttons.split(' ');for(var i=0;i "+modifierKey+"+B",command.doBold);break;case"italic":addButton("wmd-italic-button","Emphasis "+modifierKey+"+I",command.doItalic);break;case'link':addButton("wmd-link-button","Hyperlink "+modifierKey+"+L",function(chunk,postProcessing,useDefaultText){return command.doLinkOrImage(chunk,postProcessing,false);});break;case'blockquote':addButton("wmd-quote-button","Blockquote
    "+modifierKey+"+Q",command.doBlockquote);break;case'code':addButton("wmd-code-button","Code Sample
     "+modifierKey+"+K",command.doCode);break;case'image':addButton("wmd-image-button","Image  "+modifierKey+"+G",function(chunk,postProcessing,useDefaultText){return command.doLinkOrImage(chunk,postProcessing,true);});break;case'ol':addButton("wmd-olist-button","Numbered List 
      "+modifierKey+"+O",function(chunk,postProcessing,useDefaultText){command.doList(chunk,postProcessing,true,useDefaultText);});break;case'ul':addButton("wmd-ulist-button","Bulleted List