├── .gitattributes ├── MyMarkdown.php ├── README.md ├── cebe-markdown ├── GithubMarkdown.php ├── Markdown.php ├── MarkdownExtra.php ├── Parser.php ├── block │ ├── CodeTrait.php │ ├── FencedCodeTrait.php │ ├── HeadlineTrait.php │ ├── HtmlTrait.php │ ├── ListTrait.php │ ├── QuoteTrait.php │ ├── RuleTrait.php │ └── TableTrait.php └── inline │ ├── CodeTrait.php │ ├── EmphStrongTrait.php │ ├── LinkTrait.php │ ├── StrikeoutTrait.php │ └── UrlLinkTrait.php ├── metadata.json ├── pagedown ├── LICENSE.txt ├── Markdown.Converter.js ├── Markdown.Editor.js ├── Markdown.Sanitizer.js ├── highlight.min.js ├── highlightjs-run.js ├── highlightjs.css ├── markdown.min.js ├── wmd-buttons.png └── wmd.css ├── qa-markdown-editor.php ├── qa-markdown-upload.php ├── qa-markdown-viewer.php ├── qa-md-events.php ├── qa-md-lang-default.php ├── qa-md-layer.php └── qa-plugin.php /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | 4 | # Custom for Visual Studio 5 | *.cs diff=csharp 6 | *.sln merge=union 7 | *.csproj merge=union 8 | *.vbproj merge=union 9 | *.fsproj merge=union 10 | *.dbproj merge=union 11 | 12 | # Standard to msysgit 13 | *.doc diff=astextplain 14 | *.DOC diff=astextplain 15 | *.docx diff=astextplain 16 | *.DOCX diff=astextplain 17 | *.dot diff=astextplain 18 | *.DOT diff=astextplain 19 | *.pdf diff=astextplain 20 | *.PDF diff=astextplain 21 | *.rtf diff=astextplain 22 | *.RTF diff=astextplain 23 | -------------------------------------------------------------------------------- /MyMarkdown.php: -------------------------------------------------------------------------------- 1 | "
\n", "\n" => "
\n"]); 13 | } 14 | 15 | /** 16 | * Update headings to run from 2..6 to avoid multiple H1s on the page 17 | */ 18 | protected function renderHeadline($block) 19 | { 20 | $tag = 'h' . min($block['level'] + 1, 6); 21 | return "<$tag>" . $this->renderAbsy($block['content']) . "\n"; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Markdown Editor plugin for Question2Answer 2 | ================================================= 3 | 4 | This is an editor plugin for popular open source Q&A platform, [Question2Answer](http://www.question2answer.org). It uses Markdown to format posts, which is a simple text-friendly markup language using for example \*\*bold\*\* for **bold text** or \> for quoting sources. 5 | 6 | The plugin uses modified versions of the PageDown scripts (released by Stack Overflow) for the editor and live preview respectively. 7 | 8 | 9 | Installation 10 | ------------------------------------------------- 11 | 12 | 1. Download and extract the `markdown-editor` folder to the `qa-plugins` folder in your Q2A installation. 13 | 2. If your site is a different language from English, copy `qa-md-lang-default.php` to the required language code (e.g. `qa-tt-lang-de.php` for German) and edit the phrases for your language. 14 | 3. Log in to your Q2A site as a Super Administrator and head to Admin > Posting. 15 | 4. Set the default editor for questions and answers to 'Markdown Editor'. The editor does also work for comments, but keeping to plain text is recommended. 16 | 17 | In Admin > Plugins, you can set three options: 18 | 19 | - "Don't add CSS inline" - this will not output the CSS onto the page, to allow putting it in a stylesheet instead which is more efficient. Copy the CSS from `pagedown/wmd.css` to the bottom of your theme's current stylesheet. 20 | - "Plaintext comments" - Sets a post as plaintext when converting answers to comments. 21 | - "Use syntax highlighting" - Integrates [highlight.js](http://softwaremaniacs.org/soft/highlight/en/) for code blocks (including while writing posts). All common programming languages are supported, but you can add more using the [customized download here](http://softwaremaniacs.org/soft/highlight/en/download/). Save the file and overwrite `pagedown/highlight.min.js`. If you ticked the box for CSS above, copy the CSS from `pagedown/highlightjs.css` to the bottom of your theme's current stylesheet. 22 | 23 | 24 | Extra bits 25 | ------------------------------------------------- 26 | 27 | **Converting old posts:** If you have been running your Q2A site for a little while, you may wish to convert old content to Markdown. This does not work reliably for HTML content (created via the WYSIWYG editor); it is pretty safe for plain text content, but check your posts afterwards as some formatting may go awry. You can convert text posts automatically using this SQL query: 28 | 29 | UPDATE qa_posts SET format='markdown' WHERE format='' AND type IN ('Q', 'A', 'Q_HIDDEN', 'A_HIDDEN') 30 | 31 | (Make sure to change `qa_` above to your installation's table prefix if it is different.) 32 | 33 | 34 | Pay What You Like 35 | ------------------------------------------------- 36 | 37 | Most of my code is released under the open source GPLv3 license, and provided with a 'Pay What You Like' approach. Feel free to download and modify the plugins/themes to suit your needs, and I hope you value them enough to donate a few dollars. 38 | 39 | ### [Donate here](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=4R5SHBNM3UDLU) 40 | -------------------------------------------------------------------------------- /cebe-markdown/GithubMarkdown.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class GithubMarkdown extends Markdown 16 | { 17 | // include block element parsing using traits 18 | use block\TableTrait; 19 | use block\FencedCodeTrait; 20 | 21 | // include inline element parsing using traits 22 | use inline\StrikeoutTrait; 23 | use inline\UrlLinkTrait; 24 | 25 | /** 26 | * @var boolean whether to interpret newlines as `
`-tags. 27 | * This feature is useful for comments where newlines are often meant to be real new lines. 28 | */ 29 | public $enableNewlines = false; 30 | 31 | /** 32 | * @inheritDoc 33 | */ 34 | protected $escapeCharacters = [ 35 | // from Markdown 36 | '\\', // backslash 37 | '`', // backtick 38 | '*', // asterisk 39 | '_', // underscore 40 | '{', '}', // curly braces 41 | '[', ']', // square brackets 42 | '(', ')', // parentheses 43 | '#', // hash mark 44 | '+', // plus sign 45 | '-', // minus sign (hyphen) 46 | '.', // dot 47 | '!', // exclamation mark 48 | '<', '>', 49 | // added by GithubMarkdown 50 | ':', // colon 51 | '|', // pipe 52 | ]; 53 | 54 | 55 | 56 | /** 57 | * Consume lines for a paragraph 58 | * 59 | * Allow headlines, lists and code to break paragraphs 60 | */ 61 | protected function consumeParagraph($lines, $current) 62 | { 63 | // consume until newline 64 | $content = []; 65 | for ($i = $current, $count = count($lines); $i < $count; $i++) { 66 | $line = $lines[$i]; 67 | if ($line === '' 68 | || ltrim($line) === '' 69 | || !ctype_alpha($line[0]) && ( 70 | $this->identifyQuote($line, $lines, $i) || 71 | $this->identifyFencedCode($line, $lines, $i) || 72 | $this->identifyUl($line, $lines, $i) || 73 | $this->identifyOl($line, $lines, $i) || 74 | $this->identifyHr($line, $lines, $i) 75 | ) 76 | || $this->identifyHeadline($line, $lines, $i)) 77 | { 78 | break; 79 | } elseif ($this->identifyCode($line, $lines, $i)) { 80 | // possible beginning of a code block 81 | // but check for continued inline HTML 82 | // e.g. some alt aligned with src attribute 84 | if (preg_match('~<\w+([^>]+)$~s', implode("\n", $content))) { 85 | $content[] = $line; 86 | } else { 87 | break; 88 | } 89 | } else { 90 | $content[] = $line; 91 | } 92 | } 93 | $block = [ 94 | 'paragraph', 95 | 'content' => $this->parseInline(implode("\n", $content)), 96 | ]; 97 | return [$block, --$i]; 98 | } 99 | 100 | /** 101 | * @inheritdocs 102 | * 103 | * Parses a newline indicated by two spaces on the end of a markdown line. 104 | */ 105 | protected function renderText($text) 106 | { 107 | if ($this->enableNewlines) { 108 | $br = $this->html5 ? "
\n" : "
\n"; 109 | return strtr($text[1], [" \n" => $br, "\n" => $br]); 110 | } else { 111 | return parent::renderText($text); 112 | } 113 | } 114 | } 115 | -------------------------------------------------------------------------------- /cebe-markdown/Markdown.php: -------------------------------------------------------------------------------- 1 | 14 | */ 15 | class Markdown extends Parser 16 | { 17 | // include block element parsing using traits 18 | use block\CodeTrait; 19 | use block\HeadlineTrait; 20 | use block\HtmlTrait { 21 | parseInlineHtml as private; 22 | } 23 | use block\ListTrait { 24 | // Check Ul List before headline 25 | identifyUl as protected identifyBUl; 26 | consumeUl as protected consumeBUl; 27 | } 28 | use block\QuoteTrait; 29 | use block\RuleTrait { 30 | // Check Hr before checking lists 31 | identifyHr as protected identifyAHr; 32 | consumeHr as protected consumeAHr; 33 | } 34 | 35 | // include inline element parsing using traits 36 | use inline\CodeTrait; 37 | use inline\EmphStrongTrait; 38 | use inline\LinkTrait; 39 | 40 | /** 41 | * @var boolean whether to format markup according to HTML5 spec. 42 | * Defaults to `false` which means that markup is formatted as HTML4. 43 | */ 44 | public $html5 = false; 45 | 46 | /** 47 | * @var array these are "escapeable" characters. When using one of these prefixed with a 48 | * backslash, the character will be outputted without the backslash and is not interpreted 49 | * as markdown. 50 | */ 51 | protected $escapeCharacters = [ 52 | '\\', // backslash 53 | '`', // backtick 54 | '*', // asterisk 55 | '_', // underscore 56 | '{', '}', // curly braces 57 | '[', ']', // square brackets 58 | '(', ')', // parentheses 59 | '#', // hash mark 60 | '+', // plus sign 61 | '-', // minus sign (hyphen) 62 | '.', // dot 63 | '!', // exclamation mark 64 | '<', '>', 65 | ]; 66 | 67 | 68 | /** 69 | * @inheritDoc 70 | */ 71 | protected function prepare() 72 | { 73 | // reset references 74 | $this->references = []; 75 | } 76 | 77 | /** 78 | * Consume lines for a paragraph 79 | * 80 | * Allow headlines and code to break paragraphs 81 | */ 82 | protected function consumeParagraph($lines, $current) 83 | { 84 | // consume until newline 85 | $content = []; 86 | for ($i = $current, $count = count($lines); $i < $count; $i++) { 87 | $line = $lines[$i]; 88 | 89 | // a list may break a paragraph when it is inside of a list 90 | if (isset($this->context[1]) && $this->context[1] === 'list' && !ctype_alpha($line[0]) && ( 91 | $this->identifyUl($line, $lines, $i) || $this->identifyOl($line, $lines, $i))) { 92 | break; 93 | } 94 | 95 | if ($line === '' || ltrim($line) === '' || $this->identifyHeadline($line, $lines, $i)) { 96 | break; 97 | } elseif ($line[0] === "\t" || $line[0] === " " && strncmp($line, ' ', 4) === 0) { 98 | // possible beginning of a code block 99 | // but check for continued inline HTML 100 | // e.g. some alt aligned with src attribute 102 | if (preg_match('~<\w+([^>]+)$~s', implode("\n", $content))) { 103 | $content[] = $line; 104 | } else { 105 | break; 106 | } 107 | } else { 108 | $content[] = $line; 109 | } 110 | } 111 | $block = [ 112 | 'paragraph', 113 | 'content' => $this->parseInline(implode("\n", $content)), 114 | ]; 115 | return [$block, --$i]; 116 | } 117 | 118 | 119 | /** 120 | * @inheritdocs 121 | * 122 | * Parses a newline indicated by two spaces on the end of a markdown line. 123 | */ 124 | protected function renderText($text) 125 | { 126 | return str_replace(" \n", $this->html5 ? "
\n" : "
\n", $text[1]); 127 | } 128 | } 129 | -------------------------------------------------------------------------------- /cebe-markdown/MarkdownExtra.php: -------------------------------------------------------------------------------- 1 | 14 | * @license https://github.com/cebe/markdown/blob/master/LICENSE 15 | * @link https://github.com/cebe/markdown#readme 16 | */ 17 | class MarkdownExtra extends Markdown 18 | { 19 | // include block element parsing using traits 20 | use block\TableTrait; 21 | use block\FencedCodeTrait; 22 | 23 | // include inline element parsing using traits 24 | // TODO 25 | 26 | /** 27 | * @var bool whether special attributes on code blocks should be applied on the `
` element.
 28 | 	 * The default behavior is to put them on the `` element.
 29 | 	 */
 30 | 	public $codeAttributesOnPre = false;
 31 | 
 32 | 	/**
 33 | 	 * @inheritDoc
 34 | 	 */
 35 | 	protected $escapeCharacters = [
 36 | 		// from Markdown
 37 | 		'\\', // backslash
 38 | 		'`', // backtick
 39 | 		'*', // asterisk
 40 | 		'_', // underscore
 41 | 		'{', '}', // curly braces
 42 | 		'[', ']', // square brackets
 43 | 		'(', ')', // parentheses
 44 | 		'#', // hash mark
 45 | 		'+', // plus sign
 46 | 		'-', // minus sign (hyphen)
 47 | 		'.', // dot
 48 | 		'!', // exclamation mark
 49 | 		'<', '>',
 50 | 		// added by MarkdownExtra
 51 | 		':', // colon
 52 | 		'|', // pipe
 53 | 	];
 54 | 
 55 | 	private $_specialAttributesRegex = '\{(([#\.][A-z0-9-_]+\s*)+)\}';
 56 | 
 57 | 	// TODO allow HTML intended 3 spaces
 58 | 
 59 | 	// TODO add markdown inside HTML blocks
 60 | 
 61 | 	// TODO implement definition lists
 62 | 
 63 | 	// TODO implement footnotes
 64 | 
 65 | 	// TODO implement Abbreviations
 66 | 
 67 | 
 68 | 	// block parsing
 69 | 
 70 | 	protected function identifyReference($line)
 71 | 	{
 72 | 		return ($line[0] === ' ' || $line[0] === '[') && preg_match('/^ {0,3}\[(.+?)\]:\s*([^\s]+?)(?:\s+[\'"](.+?)[\'"])?\s*('.$this->_specialAttributesRegex.')?\s*$/', $line);
 73 | 	}
 74 | 
 75 | 	/**
 76 | 	 * Consume link references
 77 | 	 */
 78 | 	protected function consumeReference($lines, $current)
 79 | 	{
 80 | 		while (isset($lines[$current]) && preg_match('/^ {0,3}\[(.+?)\]:\s*(.+?)(?:\s+[\(\'"](.+?)[\)\'"])?\s*('.$this->_specialAttributesRegex.')?\s*$/', $lines[$current], $matches)) {
 81 | 			$label = strtolower($matches[1]);
 82 | 
 83 | 			$this->references[$label] = [
 84 | 				'url' => $this->replaceEscape($matches[2]),
 85 | 			];
 86 | 			if (isset($matches[3])) {
 87 | 				$this->references[$label]['title'] = $matches[3];
 88 | 			} else {
 89 | 				// title may be on the next line
 90 | 				if (isset($lines[$current + 1]) && preg_match('/^\s+[\(\'"](.+?)[\)\'"]\s*$/', $lines[$current + 1], $matches)) {
 91 | 					$this->references[$label]['title'] = $matches[1];
 92 | 					$current++;
 93 | 				}
 94 | 			}
 95 | 			if (isset($matches[5])) {
 96 | 				$this->references[$label]['attributes'] = $matches[5];
 97 | 			}
 98 | 			$current++;
 99 | 		}
100 | 		return [false, --$current];
101 | 	}
102 | 
103 | 	/**
104 | 	 * Consume lines for a fenced code block
105 | 	 */
106 | 	protected function consumeFencedCode($lines, $current)
107 | 	{
108 | 		// consume until ```
109 | 		$block = [
110 | 			'code',
111 | 		];
112 | 		$line = rtrim($lines[$current]);
113 | 		if (($pos = strrpos($line, '`')) === false) {
114 | 			$pos = strrpos($line, '~');
115 | 		}
116 | 		$fence = substr($line, 0, $pos + 1);
117 | 		$block['attributes'] = substr($line, $pos);
118 | 		$content = [];
119 | 		for($i = $current + 1, $count = count($lines); $i < $count; $i++) {
120 | 			if (rtrim($line = $lines[$i]) !== $fence) {
121 | 				$content[] = $line;
122 | 			} else {
123 | 				break;
124 | 			}
125 | 		}
126 | 		$block['content'] = implode("\n", $content);
127 | 		return [$block, $i];
128 | 	}
129 | 
130 | 	protected function renderCode($block)
131 | 	{
132 | 		$attributes = $this->renderAttributes($block);
133 | 		return ($this->codeAttributesOnPre ? "" : "
")
134 | 			. htmlspecialchars($block['content'] . "\n", ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8')
135 | 			. "
\n"; 136 | } 137 | 138 | /** 139 | * Renders a headline 140 | */ 141 | protected function renderHeadline($block) 142 | { 143 | foreach($block['content'] as $i => $element) { 144 | if ($element[0] === 'specialAttributes') { 145 | unset($block['content'][$i]); 146 | $block['attributes'] = $element[1]; 147 | } 148 | } 149 | $tag = 'h' . $block['level']; 150 | $attributes = $this->renderAttributes($block); 151 | return "<$tag$attributes>" . rtrim($this->renderAbsy($block['content']), "# \t") . "\n"; 152 | } 153 | 154 | protected function renderAttributes($block) 155 | { 156 | $html = []; 157 | if (isset($block['attributes'])) { 158 | $attributes = preg_split('/\s+/', $block['attributes'], -1, PREG_SPLIT_NO_EMPTY); 159 | foreach($attributes as $attribute) { 160 | if ($attribute[0] === '#') { 161 | $html['id'] = substr($attribute, 1); 162 | } else { 163 | $html['class'][] = substr($attribute, 1); 164 | } 165 | } 166 | } 167 | $result = ''; 168 | foreach($html as $attr => $value) { 169 | if (is_array($value)) { 170 | $value = trim(implode(' ', $value)); 171 | } 172 | if (!empty($value)) { 173 | $result .= " $attr=\"$value\""; 174 | } 175 | } 176 | return $result; 177 | } 178 | 179 | 180 | // inline parsing 181 | 182 | 183 | /** 184 | * @marker { 185 | */ 186 | protected function parseSpecialAttributes($text) 187 | { 188 | if (preg_match("~$this->_specialAttributesRegex~", $text, $matches)) { 189 | return [['specialAttributes', $matches[1]], strlen($matches[0])]; 190 | } 191 | return [['text', '{'], 1]; 192 | } 193 | 194 | protected function renderSpecialAttributes($block) 195 | { 196 | return '{' . $block[1] . '}'; 197 | } 198 | 199 | protected function parseInline($text) 200 | { 201 | $elements = parent::parseInline($text); 202 | // merge special attribute elements to links and images as they are not part of the final absy later 203 | $relatedElement = null; 204 | foreach($elements as $i => $element) { 205 | if ($element[0] === 'link' || $element[0] === 'image') { 206 | $relatedElement = $i; 207 | } elseif ($element[0] === 'specialAttributes') { 208 | if ($relatedElement !== null) { 209 | $elements[$relatedElement]['attributes'] = $element[1]; 210 | unset($elements[$i]); 211 | } 212 | $relatedElement = null; 213 | } else { 214 | $relatedElement = null; 215 | } 216 | } 217 | return $elements; 218 | } 219 | 220 | protected function renderLink($block) 221 | { 222 | if (isset($block['refkey'])) { 223 | if (($ref = $this->lookupReference($block['refkey'])) !== false) { 224 | $block = array_merge($block, $ref); 225 | } else { 226 | return $block['orig']; 227 | } 228 | } 229 | $attributes = $this->renderAttributes($block); 230 | return '' . $this->renderAbsy($block['text']) . ''; 233 | } 234 | 235 | protected function renderImage($block) 236 | { 237 | if (isset($block['refkey'])) { 238 | if (($ref = $this->lookupReference($block['refkey'])) !== false) { 239 | $block = array_merge($block, $ref); 240 | } else { 241 | return $block['orig']; 242 | } 243 | } 244 | $attributes = $this->renderAttributes($block); 245 | return '' . htmlspecialchars($block['text'], ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE, 'UTF-8') . 'html5 ? '>' : ' />'); 249 | } 250 | } -------------------------------------------------------------------------------- /cebe-markdown/Parser.php: -------------------------------------------------------------------------------- 1 | 15 | */ 16 | abstract class Parser 17 | { 18 | /** 19 | * @var integer the maximum nesting level for language elements. 20 | */ 21 | public $maximumNestingLevel = 32; 22 | 23 | /** 24 | * @var string the current context the parser is in. 25 | * TODO remove in favor of absy 26 | */ 27 | protected $context = []; 28 | /** 29 | * @var array these are "escapeable" characters. When using one of these prefixed with a 30 | * backslash, the character will be outputted without the backslash and is not interpreted 31 | * as markdown. 32 | */ 33 | protected $escapeCharacters = [ 34 | '\\', // backslash 35 | ]; 36 | 37 | private $_depth = 0; 38 | 39 | 40 | /** 41 | * Parses the given text considering the full language. 42 | * 43 | * This includes parsing block elements as well as inline elements. 44 | * 45 | * @param string $text the text to parse 46 | * @return string parsed markup 47 | */ 48 | public function parse($text) 49 | { 50 | $this->prepare(); 51 | 52 | if (ltrim($text) === '') { 53 | return ''; 54 | } 55 | 56 | $text = str_replace(["\r\n", "\n\r", "\r"], "\n", $text); 57 | 58 | $this->prepareMarkers($text); 59 | 60 | $absy = $this->parseBlocks(explode("\n", $text)); 61 | $markup = $this->renderAbsy($absy); 62 | 63 | $this->cleanup(); 64 | return $markup; 65 | } 66 | 67 | /** 68 | * Parses a paragraph without block elements (block elements are ignored). 69 | * 70 | * @param string $text the text to parse 71 | * @return string parsed markup 72 | */ 73 | public function parseParagraph($text) 74 | { 75 | $this->prepare(); 76 | 77 | if (ltrim($text) === '') { 78 | return ''; 79 | } 80 | 81 | $text = str_replace(["\r\n", "\n\r", "\r"], "\n", $text); 82 | 83 | $this->prepareMarkers($text); 84 | 85 | $absy = $this->parseInline($text); 86 | $markup = $this->renderAbsy($absy); 87 | 88 | $this->cleanup(); 89 | return $markup; 90 | } 91 | 92 | /** 93 | * This method will be called before `parse()` and `parseParagraph()`. 94 | * You can override it to do some initialization work. 95 | */ 96 | protected function prepare() 97 | { 98 | } 99 | 100 | /** 101 | * This method will be called after `parse()` and `parseParagraph()`. 102 | * You can override it to do cleanup. 103 | */ 104 | protected function cleanup() 105 | { 106 | } 107 | 108 | 109 | // block parsing 110 | 111 | private $_blockTypes; 112 | 113 | /** 114 | * @return array a list of block element types available. 115 | */ 116 | protected function blockTypes() 117 | { 118 | if ($this->_blockTypes === null) { 119 | // detect block types via "identify" functions 120 | $reflection = new \ReflectionClass($this); 121 | $this->_blockTypes = array_filter(array_map(function($method) { 122 | $name = $method->getName(); 123 | return strncmp($name, 'identify', 8) === 0 ? strtolower(substr($name, 8)) : false; 124 | }, $reflection->getMethods(ReflectionMethod::IS_PROTECTED))); 125 | 126 | sort($this->_blockTypes); 127 | } 128 | return $this->_blockTypes; 129 | } 130 | 131 | /** 132 | * Given a set of lines and an index of a current line it uses the registed block types to 133 | * detect the type of this line. 134 | * @param array $lines 135 | * @param integer $current 136 | * @return string name of the block type in lower case 137 | */ 138 | protected function detectLineType($lines, $current) 139 | { 140 | $line = $lines[$current]; 141 | $blockTypes = $this->blockTypes(); 142 | foreach($blockTypes as $blockType) { 143 | if ($this->{'identify' . $blockType}($line, $lines, $current)) { 144 | return $blockType; 145 | } 146 | } 147 | // consider the line a normal paragraph if no other block type matches 148 | return 'paragraph'; 149 | } 150 | 151 | /** 152 | * Parse block elements by calling `detectLineType()` to identify them 153 | * and call consume function afterwards. 154 | */ 155 | protected function parseBlocks($lines) 156 | { 157 | if ($this->_depth >= $this->maximumNestingLevel) { 158 | // maximum depth is reached, do not parse input 159 | return [['text', implode("\n", $lines)]]; 160 | } 161 | $this->_depth++; 162 | 163 | $blocks = []; 164 | 165 | // convert lines to blocks 166 | for ($i = 0, $count = count($lines); $i < $count; $i++) { 167 | $line = $lines[$i]; 168 | if ($line !== '' && rtrim($line) !== '') { // skip empty lines 169 | // identify a blocks beginning and parse the content 170 | list($block, $i) = $this->parseBlock($lines, $i); 171 | if ($block !== false) { 172 | $blocks[] = $block; 173 | } 174 | } 175 | } 176 | 177 | $this->_depth--; 178 | 179 | return $blocks; 180 | } 181 | 182 | /** 183 | * Parses the block at current line by identifying the block type and parsing the content 184 | * @param $lines 185 | * @param $current 186 | * @return array Array of two elements, the first element contains the block, 187 | * the second contains the next line index to be parsed. 188 | */ 189 | protected function parseBlock($lines, $current) 190 | { 191 | // identify block type for this line 192 | $blockType = $this->detectLineType($lines, $current); 193 | 194 | // call consume method for the detected block type to consume further lines 195 | return $this->{'consume' . $blockType}($lines, $current); 196 | } 197 | 198 | protected function renderAbsy($blocks) 199 | { 200 | $output = ''; 201 | foreach ($blocks as $block) { 202 | array_unshift($this->context, $block[0]); 203 | $output .= $this->{'render' . $block[0]}($block); 204 | array_shift($this->context); 205 | } 206 | return $output; 207 | } 208 | 209 | /** 210 | * Consume lines for a paragraph 211 | * 212 | * @param $lines 213 | * @param $current 214 | * @return array 215 | */ 216 | protected function consumeParagraph($lines, $current) 217 | { 218 | // consume until newline 219 | $content = []; 220 | for ($i = $current, $count = count($lines); $i < $count; $i++) { 221 | if (ltrim($lines[$i]) !== '') { 222 | $content[] = $lines[$i]; 223 | } else { 224 | break; 225 | } 226 | } 227 | $block = [ 228 | 'paragraph', 229 | 'content' => $this->parseInline(implode("\n", $content)), 230 | ]; 231 | return [$block, --$i]; 232 | } 233 | 234 | /** 235 | * Render a paragraph block 236 | * 237 | * @param $block 238 | * @return string 239 | */ 240 | protected function renderParagraph($block) 241 | { 242 | return '

' . $this->renderAbsy($block['content']) . "

\n"; 243 | } 244 | 245 | 246 | // inline parsing 247 | 248 | 249 | /** 250 | * @var array the set of inline markers to use in different contexts. 251 | */ 252 | private $_inlineMarkers = []; 253 | 254 | /** 255 | * Returns a map of inline markers to the corresponding parser methods. 256 | * 257 | * This array defines handler methods for inline markdown markers. 258 | * When a marker is found in the text, the handler method is called with the text 259 | * starting at the position of the marker. 260 | * 261 | * Note that markers starting with whitespace may slow down the parser, 262 | * you may want to use [[renderText]] to deal with them. 263 | * 264 | * You may override this method to define a set of markers and parsing methods. 265 | * The default implementation looks for protected methods starting with `parse` that 266 | * also have an `@marker` annotation in PHPDoc. 267 | * 268 | * @return array a map of markers to parser methods 269 | */ 270 | protected function inlineMarkers() 271 | { 272 | $markers = []; 273 | // detect "parse" functions 274 | $reflection = new \ReflectionClass($this); 275 | foreach($reflection->getMethods(ReflectionMethod::IS_PROTECTED) as $method) { 276 | $methodName = $method->getName(); 277 | if (strncmp($methodName, 'parse', 5) === 0) { 278 | preg_match_all('/@marker ([^\s]+)/', $method->getDocComment(), $matches); 279 | foreach($matches[1] as $match) { 280 | $markers[$match] = $methodName; 281 | } 282 | } 283 | } 284 | return $markers; 285 | } 286 | 287 | /** 288 | * Prepare markers that are used in the text to parse 289 | * 290 | * Add all markers that are present in markdown. 291 | * Check is done to avoid iterations in parseInline(), good for huge markdown files 292 | * @param string $text 293 | */ 294 | protected function prepareMarkers($text) 295 | { 296 | $this->_inlineMarkers = []; 297 | foreach ($this->inlineMarkers() as $marker => $method) { 298 | if (strpos($text, $marker) !== false) { 299 | $m = $marker[0]; 300 | // put the longest marker first 301 | if (isset($this->_inlineMarkers[$m])) { 302 | reset($this->_inlineMarkers[$m]); 303 | if (strlen($marker) > strlen(key($this->_inlineMarkers[$m]))) { 304 | $this->_inlineMarkers[$m] = array_merge([$marker => $method], $this->_inlineMarkers[$m]); 305 | continue; 306 | } 307 | } 308 | $this->_inlineMarkers[$m][$marker] = $method; 309 | } 310 | } 311 | } 312 | 313 | /** 314 | * Parses inline elements of the language. 315 | * 316 | * @param string $text the inline text to parse. 317 | * @return array 318 | */ 319 | protected function parseInline($text) 320 | { 321 | if ($this->_depth >= $this->maximumNestingLevel) { 322 | // maximum depth is reached, do not parse input 323 | return [['text', $text]]; 324 | } 325 | $this->_depth++; 326 | 327 | $markers = implode('', array_keys($this->_inlineMarkers)); 328 | 329 | $paragraph = []; 330 | 331 | while (!empty($markers) && ($found = strpbrk($text, $markers)) !== false) { 332 | 333 | $pos = strpos($text, $found); 334 | 335 | // add the text up to next marker to the paragraph 336 | if ($pos !== 0) { 337 | $paragraph[] = ['text', substr($text, 0, $pos)]; 338 | } 339 | $text = $found; 340 | 341 | $parsed = false; 342 | foreach ($this->_inlineMarkers[$text[0]] as $marker => $method) { 343 | if (strncmp($text, $marker, strlen($marker)) === 0) { 344 | // parse the marker 345 | array_unshift($this->context, $method); 346 | list($output, $offset) = $this->$method($text); 347 | array_shift($this->context); 348 | 349 | $paragraph[] = $output; 350 | $text = substr($text, $offset); 351 | $parsed = true; 352 | break; 353 | } 354 | } 355 | if (!$parsed) { 356 | $paragraph[] = ['text', substr($text, 0, 1)]; 357 | $text = substr($text, 1); 358 | } 359 | } 360 | 361 | $paragraph[] = ['text', $text]; 362 | 363 | $this->_depth--; 364 | 365 | return $paragraph; 366 | } 367 | 368 | /** 369 | * Parses escaped special characters. 370 | * @marker \ 371 | */ 372 | protected function parseEscape($text) 373 | { 374 | if (isset($text[1]) && in_array($text[1], $this->escapeCharacters)) { 375 | return [['text', $text[1]], 2]; 376 | } 377 | return [['text', $text[0]], 1]; 378 | } 379 | 380 | /** 381 | * This function renders plain text sections in the markdown text. 382 | * It can be used to work on normal text sections for example to highlight keywords or 383 | * do special escaping. 384 | */ 385 | protected function renderText($block) 386 | { 387 | return $block[1]; 388 | } 389 | } 390 | -------------------------------------------------------------------------------- /cebe-markdown/block/CodeTrait.php: -------------------------------------------------------------------------------- 1 | = 4 or one tab is code 21 | return ($l = $line[0]) === ' ' && $line[1] === ' ' && $line[2] === ' ' && $line[3] === ' ' || $l === "\t"; 22 | } 23 | 24 | /** 25 | * Consume lines for a code block element 26 | */ 27 | protected function consumeCode($lines, $current) 28 | { 29 | // consume until newline 30 | 31 | $content = []; 32 | for ($i = $current, $count = count($lines); $i < $count; $i++) { 33 | $line = $lines[$i]; 34 | 35 | // a line is considered to belong to this code block as long as it is intended by 4 spaces or a tab 36 | if (isset($line[0]) && ($line[0] === "\t" || strncmp($line, ' ', 4) === 0)) { 37 | $line = $line[0] === "\t" ? substr($line, 1) : substr($line, 4); 38 | $content[] = $line; 39 | // but also if it is empty and the next line is intended by 4 spaces or a tab 40 | } elseif (($line === '' || rtrim($line) === '') && isset($lines[$i + 1][0]) && 41 | ($lines[$i + 1][0] === "\t" || strncmp($lines[$i + 1], ' ', 4) === 0)) { 42 | if ($line !== '') { 43 | $line = $line[0] === "\t" ? substr($line, 1) : substr($line, 4); 44 | } 45 | $content[] = $line; 46 | } else { 47 | break; 48 | } 49 | } 50 | 51 | $block = [ 52 | 'code', 53 | 'content' => implode("\n", $content), 54 | ]; 55 | return [$block, --$i]; 56 | } 57 | 58 | /** 59 | * Renders a code block 60 | */ 61 | protected function renderCode($block) 62 | { 63 | $class = isset($block['language']) ? ' class="language-' . $block['language'] . '"' : ''; 64 | return "
" . htmlspecialchars($block['content'] . "\n", ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8') . "
\n"; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /cebe-markdown/block/FencedCodeTrait.php: -------------------------------------------------------------------------------- 1 | implode("\n", $content), 48 | ]; 49 | if (!empty($language)) { 50 | $block['language'] = $language; 51 | } 52 | return [$block, $i]; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /cebe-markdown/block/HeadlineTrait.php: -------------------------------------------------------------------------------- 1 | $this->parseInline(trim($lines[$current], "# \t")), 45 | 'level' => $level, 46 | ]; 47 | return [$block, $current]; 48 | } else { 49 | // underlined headline 50 | $block = [ 51 | 'headline', 52 | 'content' => $this->parseInline($lines[$current]), 53 | 'level' => $lines[$current + 1][0] === '=' ? 1 : 2, 54 | ]; 55 | return [$block, $current + 1]; 56 | } 57 | } 58 | 59 | /** 60 | * Renders a headline 61 | */ 62 | protected function renderHeadline($block) 63 | { 64 | $tag = 'h' . $block['level']; 65 | return "<$tag>" . $this->renderAbsy($block['content']) . "\n"; 66 | } 67 | 68 | abstract protected function parseInline($text); 69 | abstract protected function renderAbsy($absy); 70 | } 71 | -------------------------------------------------------------------------------- /cebe-markdown/block/HtmlTrait.php: -------------------------------------------------------------------------------- 1 | '); 61 | $spacePos = strpos($lines[$current], ' '); 62 | if ($gtPos === false && $spacePos === false) { 63 | return false; // no html tag 64 | } elseif ($spacePos === false) { 65 | $tag = rtrim(substr($line, 1, $gtPos - 1), '/'); 66 | } else { 67 | $tag = rtrim(substr($line, 1, min($gtPos, $spacePos) - 1), '/'); 68 | } 69 | 70 | if (!ctype_alnum($tag) || in_array(strtolower($tag), $this->inlineHtmlElements)) { 71 | return false; // no html tag or inline html tag 72 | } 73 | return true; 74 | } 75 | 76 | /** 77 | * Consume lines for an HTML block 78 | */ 79 | protected function consumeHtml($lines, $current) 80 | { 81 | $content = []; 82 | if (strncmp($lines[$current], '') !== false) { 87 | break; 88 | } 89 | } 90 | } else { 91 | $tag = rtrim(substr($lines[$current], 1, min(strpos($lines[$current], '>'), strpos($lines[$current] . ' ', ' ')) - 1), '/'); 92 | $level = 0; 93 | if (in_array($tag, $this->selfClosingHtmlElements)) { 94 | $level--; 95 | } 96 | for ($i = $current, $count = count($lines); $i < $count; $i++) { 97 | $line = $lines[$i]; 98 | $content[] = $line; 99 | $level += substr_count($line, "<$tag") - substr_count($line, "") - substr_count($line, "/>"); 100 | if ($level <= 0) { 101 | break; 102 | } 103 | } 104 | } 105 | $block = [ 106 | 'html', 107 | 'content' => implode("\n", $content), 108 | ]; 109 | return [$block, $i]; 110 | } 111 | 112 | /** 113 | * Renders an HTML block 114 | */ 115 | protected function renderHtml($block) 116 | { 117 | return $block['content'] . "\n"; 118 | } 119 | 120 | /** 121 | * Parses an & or a html entity definition. 122 | * @marker & 123 | */ 124 | protected function parseEntity($text) 125 | { 126 | // html entities e.g. © © © 127 | if (preg_match('/^&#?[\w\d]+;/', $text, $matches)) { 128 | return [['inlineHtml', $matches[0]], strlen($matches[0])]; 129 | } else { 130 | return [['text', '&'], 1]; 131 | } 132 | } 133 | 134 | /** 135 | * renders a html entity. 136 | */ 137 | protected function renderInlineHtml($block) 138 | { 139 | return $block[1]; 140 | } 141 | 142 | /** 143 | * Parses inline HTML. 144 | * @marker < 145 | */ 146 | protected function parseInlineHtml($text) 147 | { 148 | if (strpos($text, '>') !== false) { 149 | if (preg_match('~^~s', $text, $matches)) { 150 | // HTML tags 151 | return [['inlineHtml', $matches[0]], strlen($matches[0])]; 152 | } elseif (preg_match('~^~s', $text, $matches)) { 153 | // HTML comments 154 | return [['inlineHtml', $matches[0]], strlen($matches[0])]; 155 | } 156 | } 157 | return [['text', '<'], 1]; 158 | } 159 | 160 | /** 161 | * Escapes `>` characters. 162 | * @marker > 163 | */ 164 | protected function parseGt($text) 165 | { 166 | return [['text', '>'], 1]; 167 | } 168 | } 169 | -------------------------------------------------------------------------------- /cebe-markdown/block/ListTrait.php: -------------------------------------------------------------------------------- 1 | ) starts with 1. 19 | */ 20 | public $keepListStartNumber = false; 21 | 22 | /** 23 | * identify a line as the beginning of an ordered list. 24 | */ 25 | protected function identifyOl($line) 26 | { 27 | return (($l = $line[0]) > '0' && $l <= '9' || $l === ' ') && preg_match('/^ {0,3}\d+\.[ \t]/', $line); 28 | } 29 | 30 | /** 31 | * identify a line as the beginning of an unordered list. 32 | */ 33 | protected function identifyUl($line) 34 | { 35 | $l = $line[0]; 36 | return ($l === '-' || $l === '+' || $l === '*') && (isset($line[1]) && (($l1 = $line[1]) === ' ' || $l1 === "\t")) || 37 | ($l === ' ' && preg_match('/^ {0,3}[\-\+\*][ \t]/', $line)); 38 | } 39 | 40 | /** 41 | * Consume lines for an ordered list 42 | */ 43 | protected function consumeOl($lines, $current) 44 | { 45 | // consume until newline 46 | 47 | $block = [ 48 | 'list', 49 | 'list' => 'ol', 50 | 'attr' => [], 51 | 'items' => [], 52 | ]; 53 | return $this->consumeList($lines, $current, $block, 'ol'); 54 | } 55 | 56 | /** 57 | * Consume lines for an unordered list 58 | */ 59 | protected function consumeUl($lines, $current) 60 | { 61 | // consume until newline 62 | 63 | $block = [ 64 | 'list', 65 | 'list' => 'ul', 66 | 'items' => [], 67 | ]; 68 | return $this->consumeList($lines, $current, $block, 'ul'); 69 | } 70 | 71 | private function consumeList($lines, $current, $block, $type) 72 | { 73 | $item = 0; 74 | $indent = ''; 75 | $len = 0; 76 | $lastLineEmpty = false; 77 | // track the indentation of list markers, if indented more than previous element 78 | // a list marker is considered to be long to a lower level 79 | $leadSpace = 3; 80 | $marker = $type === 'ul' ? ltrim($lines[$current])[0] : ''; 81 | for ($i = $current, $count = count($lines); $i < $count; $i++) { 82 | $line = $lines[$i]; 83 | // match list marker on the beginning of the line 84 | $pattern = ($type == 'ol') ? '/^( {0,'.$leadSpace.'})(\d+)\.[ \t]+/' : '/^( {0,'.$leadSpace.'})\\'.$marker.'[ \t]+/'; 85 | if (preg_match($pattern, $line, $matches)) { 86 | if (($len = substr_count($matches[0], "\t")) > 0) { 87 | $indent = str_repeat("\t", $len); 88 | $line = substr($line, strlen($matches[0])); 89 | } else { 90 | $len = strlen($matches[0]); 91 | $indent = str_repeat(' ', $len); 92 | $line = substr($line, $len); 93 | } 94 | if ($i === $current) { 95 | $leadSpace = strlen($matches[1]) + 1; 96 | } 97 | 98 | if ($type == 'ol' && $this->keepListStartNumber) { 99 | // attr `start` for ol 100 | if (!isset($block['attr']['start']) && isset($matches[2])) { 101 | $block['attr']['start'] = $matches[2]; 102 | } 103 | } 104 | 105 | $block['items'][++$item][] = $line; 106 | $block['lazyItems'][$item] = $lastLineEmpty; 107 | $lastLineEmpty = false; 108 | } elseif (ltrim($line) === '') { 109 | // line is empty, may be a lazy list 110 | $lastLineEmpty = true; 111 | 112 | // two empty lines will end the list 113 | if (!isset($lines[$i + 1][0])) { 114 | break; 115 | 116 | // next item is the continuation of this list -> lazy list 117 | } elseif (preg_match($pattern, $lines[$i + 1])) { 118 | $block['items'][$item][] = $line; 119 | $block['lazyItems'][$item] = true; 120 | 121 | // next item is indented as much as this list -> lazy list if it is not a reference 122 | } elseif (strncmp($lines[$i + 1], $indent, $len) === 0 || !empty($lines[$i + 1]) && $lines[$i + 1][0] == "\t") { 123 | $block['items'][$item][] = $line; 124 | $nextLine = $lines[$i + 1][0] === "\t" ? substr($lines[$i + 1], 1) : substr($lines[$i + 1], $len); 125 | $block['lazyItems'][$item] = !method_exists($this, 'identifyReference') || !$this->identifyReference($nextLine); 126 | 127 | // everything else ends the list 128 | } else { 129 | break; 130 | } 131 | } else { 132 | if ($line[0] === "\t") { 133 | $line = substr($line, 1); 134 | } elseif (strncmp($line, $indent, $len) === 0) { 135 | $line = substr($line, $len); 136 | } 137 | $block['items'][$item][] = $line; 138 | $lastLineEmpty = false; 139 | } 140 | } 141 | 142 | foreach($block['items'] as $itemId => $itemLines) { 143 | $content = []; 144 | if (!$block['lazyItems'][$itemId]) { 145 | $firstPar = []; 146 | while (!empty($itemLines) && rtrim($itemLines[0]) !== '' && $this->detectLineType($itemLines, 0) === 'paragraph') { 147 | $firstPar[] = array_shift($itemLines); 148 | } 149 | $content = $this->parseInline(implode("\n", $firstPar)); 150 | } 151 | if (!empty($itemLines)) { 152 | $content = array_merge($content, $this->parseBlocks($itemLines)); 153 | } 154 | $block['items'][$itemId] = $content; 155 | } 156 | 157 | return [$block, $i]; 158 | } 159 | 160 | /** 161 | * Renders a list 162 | */ 163 | protected function renderList($block) 164 | { 165 | $type = $block['list']; 166 | 167 | if (!empty($block['attr'])) { 168 | $output = "<$type " . $this->generateHtmlAttributes($block['attr']) . ">\n"; 169 | } else { 170 | $output = "<$type>\n"; 171 | } 172 | 173 | foreach ($block['items'] as $item => $itemLines) { 174 | $output .= '
  • ' . $this->renderAbsy($itemLines). "
  • \n"; 175 | } 176 | return $output . "\n"; 177 | } 178 | 179 | 180 | /** 181 | * Return html attributes string from [attrName => attrValue] list 182 | * @param array $attributes the attribute name-value pairs. 183 | * @return string 184 | */ 185 | private function generateHtmlAttributes($attributes) 186 | { 187 | foreach ($attributes as $name => $value) { 188 | $attributes[$name] = "$name=\"$value\""; 189 | } 190 | return implode(' ', $attributes); 191 | } 192 | 193 | abstract protected function parseBlocks($lines); 194 | abstract protected function parseInline($text); 195 | abstract protected function renderAbsy($absy); 196 | abstract protected function detectLineType($lines, $current); 197 | } 198 | -------------------------------------------------------------------------------- /cebe-markdown/block/QuoteTrait.php: -------------------------------------------------------------------------------- 1 | ' && (!isset($line[1]) || ($l1 = $line[1]) === ' ' || $l1 === "\t"); 21 | } 22 | 23 | /** 24 | * Consume lines for a blockquote element 25 | */ 26 | protected function consumeQuote($lines, $current) 27 | { 28 | // consume until newline 29 | $content = []; 30 | for ($i = $current, $count = count($lines); $i < $count; $i++) { 31 | $line = $lines[$i]; 32 | if (ltrim($line) !== '') { 33 | if ($line[0] == '>' && !isset($line[1])) { 34 | $line = ''; 35 | } elseif (strncmp($line, '> ', 2) === 0) { 36 | $line = substr($line, 2); 37 | } 38 | $content[] = $line; 39 | } else { 40 | break; 41 | } 42 | } 43 | 44 | $block = [ 45 | 'quote', 46 | 'content' => $this->parseBlocks($content), 47 | 'simple' => true, 48 | ]; 49 | return [$block, $i]; 50 | } 51 | 52 | 53 | /** 54 | * Renders a blockquote 55 | */ 56 | protected function renderQuote($block) 57 | { 58 | return '
    ' . $this->renderAbsy($block['content']) . "
    \n"; 59 | } 60 | 61 | abstract protected function parseBlocks($lines); 62 | abstract protected function renderAbsy($absy); 63 | } 64 | -------------------------------------------------------------------------------- /cebe-markdown/block/RuleTrait.php: -------------------------------------------------------------------------------- 1 | html5 ? "
    \n" : "
    \n"; 38 | } 39 | 40 | } -------------------------------------------------------------------------------- /cebe-markdown/block/TableTrait.php: -------------------------------------------------------------------------------- 1 | [], 38 | 'rows' => [], 39 | ]; 40 | $beginsWithPipe = $lines[$current][0] === '|'; 41 | for ($i = $current, $count = count($lines); $i < $count; $i++) { 42 | $line = rtrim($lines[$i]); 43 | 44 | // extract alignment from second line 45 | if ($i == $current+1) { 46 | $cols = explode('|', trim($line, ' |')); 47 | foreach($cols as $col) { 48 | $col = trim($col); 49 | if (empty($col)) { 50 | $block['cols'][] = ''; 51 | continue; 52 | } 53 | $l = ($col[0] === ':'); 54 | $r = (substr($col, -1, 1) === ':'); 55 | if ($l && $r) { 56 | $block['cols'][] = 'center'; 57 | } elseif ($l) { 58 | $block['cols'][] = 'left'; 59 | } elseif ($r) { 60 | $block['cols'][] = 'right'; 61 | } else { 62 | $block['cols'][] = ''; 63 | } 64 | } 65 | 66 | continue; 67 | } 68 | if ($line === '' || $beginsWithPipe && $line[0] !== '|') { 69 | break; 70 | } 71 | if ($line[0] === '|') { 72 | $line = substr($line, 1); 73 | } 74 | if (substr($line, -1, 1) === '|' && (substr($line, -2, 2) !== '\\|' || substr($line, -3, 3) === '\\\\|')) { 75 | $line = substr($line, 0, -1); 76 | } 77 | $block['rows'][] = $line; 78 | } 79 | 80 | return [$block, --$i]; 81 | } 82 | 83 | /** 84 | * render a table block 85 | */ 86 | protected function renderTable($block) 87 | { 88 | $content = ''; 89 | $this->_tableCellAlign = $block['cols']; 90 | $content .= "\n"; 91 | $first = true; 92 | foreach($block['rows'] as $row) { 93 | $this->_tableCellTag = $first ? 'th' : 'td'; 94 | $align = empty($this->_tableCellAlign[$this->_tableCellCount]) ? '' : ' align="' . $this->_tableCellAlign[$this->_tableCellCount++] . '"'; 95 | $tds = "<$this->_tableCellTag$align>" . trim($this->renderAbsy($this->parseInline($row))) . "_tableCellTag>"; // TODO move this to the consume step 96 | $content .= "$tds\n"; 97 | if ($first) { 98 | $content .= "\n\n"; 99 | } 100 | $first = false; 101 | $this->_tableCellCount = 0; 102 | } 103 | return "\n$content\n
    \n"; 104 | } 105 | 106 | /** 107 | * @marker | 108 | */ 109 | protected function parseTd($markdown) 110 | { 111 | if (isset($this->context[1]) && $this->context[1] === 'table') { 112 | $align = empty($this->_tableCellAlign[$this->_tableCellCount]) ? '' : ' align="' . $this->_tableCellAlign[$this->_tableCellCount++] . '"'; 113 | return [['text', "_tableCellTag><$this->_tableCellTag$align>"], isset($markdown[1]) && $markdown[1] === ' ' ? 2 : 1]; // TODO make a absy node 114 | } 115 | return [['text', $markdown[0]], 1]; 116 | } 117 | 118 | abstract protected function parseInline($text); 119 | abstract protected function renderAbsy($absy); 120 | } 121 | -------------------------------------------------------------------------------- /cebe-markdown/inline/CodeTrait.php: -------------------------------------------------------------------------------- 1 | ' . htmlspecialchars($block[1], ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8') . ''; 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /cebe-markdown/inline/EmphStrongTrait.php: -------------------------------------------------------------------------------- 1 | parseInline($matches[1]), 36 | ], 37 | strlen($matches[0]) 38 | ]; 39 | } 40 | } else { // emph 41 | if ($marker == '*' && preg_match('/^[*]((?:[^*]|[*][*][^*]+?[*][*])+?)[*](?![*][^*])/s', $text, $matches) || 42 | $marker == '_' && preg_match('/^_((?:[^_]|__[^_]*__)+?)_(?!_[^_])\b/us', $text, $matches)) { 43 | return [ 44 | [ 45 | 'emph', 46 | $this->parseInline($matches[1]), 47 | ], 48 | strlen($matches[0]) 49 | ]; 50 | } 51 | } 52 | return [['text', $text[0]], 1]; 53 | } 54 | 55 | protected function renderStrong($block) 56 | { 57 | return '' . $this->renderAbsy($block[1]) . ''; 58 | } 59 | 60 | protected function renderEmph($block) 61 | { 62 | return '' . $this->renderAbsy($block[1]) . ''; 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /cebe-markdown/inline/LinkTrait.php: -------------------------------------------------------------------------------- 1 | references = []; 35 | * } 36 | * ``` 37 | */ 38 | trait LinkTrait 39 | { 40 | /** 41 | * @var array a list of defined references in this document. 42 | */ 43 | protected $references = []; 44 | 45 | /** 46 | * Remove backslash from escaped characters 47 | * @param $text 48 | * @return string 49 | */ 50 | protected function replaceEscape($text) 51 | { 52 | $strtr = []; 53 | foreach($this->escapeCharacters as $char) { 54 | $strtr["\\$char"] = $char; 55 | } 56 | return strtr($text, $strtr); 57 | } 58 | 59 | /** 60 | * Parses a link indicated by `[`. 61 | * @marker [ 62 | */ 63 | protected function parseLink($markdown) 64 | { 65 | if (!in_array('parseLink', array_slice($this->context, 1)) && ($parts = $this->parseLinkOrImage($markdown)) !== false) { 66 | list($text, $url, $title, $offset, $key) = $parts; 67 | return [ 68 | [ 69 | 'link', 70 | 'text' => $this->parseInline($text), 71 | 'url' => $url, 72 | 'title' => $title, 73 | 'refkey' => $key, 74 | 'orig' => substr($markdown, 0, $offset), 75 | ], 76 | $offset 77 | ]; 78 | } else { 79 | // remove all starting [ markers to avoid next one to be parsed as link 80 | $result = '['; 81 | $i = 1; 82 | while (isset($markdown[$i]) && $markdown[$i] == '[') { 83 | $result .= '['; 84 | $i++; 85 | } 86 | return [['text', $result], $i]; 87 | } 88 | } 89 | 90 | /** 91 | * Parses an image indicated by `![`. 92 | * @marker ![ 93 | */ 94 | protected function parseImage($markdown) 95 | { 96 | if (($parts = $this->parseLinkOrImage(substr($markdown, 1))) !== false) { 97 | list($text, $url, $title, $offset, $key) = $parts; 98 | 99 | return [ 100 | [ 101 | 'image', 102 | 'text' => $text, 103 | 'url' => $url, 104 | 'title' => $title, 105 | 'refkey' => $key, 106 | 'orig' => substr($markdown, 0, $offset + 1), 107 | ], 108 | $offset + 1 109 | ]; 110 | } else { 111 | // remove all starting [ markers to avoid next one to be parsed as link 112 | $result = '!'; 113 | $i = 1; 114 | while (isset($markdown[$i]) && $markdown[$i] == '[') { 115 | $result .= '['; 116 | $i++; 117 | } 118 | return [['text', $result], $i]; 119 | } 120 | } 121 | 122 | protected function parseLinkOrImage($markdown) 123 | { 124 | if (strpos($markdown, ']') !== false && preg_match('/\[((?>[^\]\[]+|(?R))*)\]/', $markdown, $textMatches)) { // TODO improve bracket regex 125 | $text = $textMatches[1]; 126 | $offset = strlen($textMatches[0]); 127 | $markdown = substr($markdown, $offset); 128 | 129 | $pattern = <<[^\s()]+)|(?R))*\) 132 | | # else match a link with title 133 | ^\(\s*(((?>[^\s()]+)|(?R))*)(\s+"(.*?)")?\s*\) 134 | )/x 135 | REGEXP; 136 | if (preg_match($pattern, $markdown, $refMatches)) { 137 | // inline link 138 | return [ 139 | $text, 140 | isset($refMatches[2]) ? $this->replaceEscape($refMatches[2]) : '', // url 141 | empty($refMatches[5]) ? null: $refMatches[5], // title 142 | $offset + strlen($refMatches[0]), // offset 143 | null, // reference key 144 | ]; 145 | } elseif (preg_match('/^([ \n]?\[(.*?)\])?/s', $markdown, $refMatches)) { 146 | // reference style link 147 | if (empty($refMatches[2])) { 148 | $key = strtolower($text); 149 | } else { 150 | $key = strtolower($refMatches[2]); 151 | } 152 | return [ 153 | $text, 154 | null, // url 155 | null, // title 156 | $offset + strlen($refMatches[0]), // offset 157 | $key, 158 | ]; 159 | } 160 | } 161 | return false; 162 | } 163 | 164 | /** 165 | * Parses inline HTML. 166 | * @marker < 167 | */ 168 | protected function parseLt($text) 169 | { 170 | if (strpos($text, '>') !== false) { 171 | if (!in_array('parseLink', $this->context)) { // do not allow links in links 172 | if (preg_match('/^<([^\s]*?@[^\s]*?\.\w+?)>/', $text, $matches)) { 173 | // email address 174 | return [ 175 | ['email', $this->replaceEscape($matches[1])], 176 | strlen($matches[0]) 177 | ]; 178 | } elseif (preg_match('/^<([a-z]{3,}:\/\/[^\s]+?)>/', $text, $matches)) { 179 | // URL 180 | return [ 181 | ['url', $this->replaceEscape($matches[1])], 182 | strlen($matches[0]) 183 | ]; 184 | } 185 | } 186 | // try inline HTML if it was neither a URL nor email if HtmlTrait is included. 187 | if (method_exists($this, 'parseInlineHtml')) { 188 | return $this->parseInlineHtml($text); 189 | } 190 | } 191 | return [['text', '<'], 1]; 192 | } 193 | 194 | protected function renderEmail($block) 195 | { 196 | $email = htmlspecialchars($block[1], ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8'); 197 | return "$email"; 198 | } 199 | 200 | protected function renderUrl($block) 201 | { 202 | $url = htmlspecialchars($block[1], ENT_COMPAT | ENT_HTML401, 'UTF-8'); 203 | $text = htmlspecialchars(urldecode($block[1]), ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8'); 204 | return "$text"; 205 | } 206 | 207 | protected function lookupReference($key) 208 | { 209 | $normalizedKey = preg_replace('/\s+/', ' ', $key); 210 | if (isset($this->references[$key]) || isset($this->references[$key = $normalizedKey])) { 211 | return $this->references[$key]; 212 | } 213 | return false; 214 | } 215 | 216 | protected function renderLink($block) 217 | { 218 | if (isset($block['refkey'])) { 219 | if (($ref = $this->lookupReference($block['refkey'])) !== false) { 220 | $block = array_merge($block, $ref); 221 | } else { 222 | return $block['orig']; 223 | } 224 | } 225 | return '' . $this->renderAbsy($block['text']) . ''; 228 | } 229 | 230 | protected function renderImage($block) 231 | { 232 | if (isset($block['refkey'])) { 233 | if (($ref = $this->lookupReference($block['refkey'])) !== false) { 234 | $block = array_merge($block, $ref); 235 | } else { 236 | return $block['orig']; 237 | } 238 | } 239 | return '' . htmlspecialchars($block['text'], ENT_COMPAT | ENT_HTML401 | ENT_SUBSTITUTE, 'UTF-8') . 'html5 ? '>' : ' />'); 243 | } 244 | 245 | // references 246 | 247 | protected function identifyReference($line) 248 | { 249 | return ($line[0] === ' ' || $line[0] === '[') && preg_match('/^ {0,3}\[(.+?)\]:\s*([^\s]+?)(?:\s+[\'"](.+?)[\'"])?\s*$/', $line); 250 | } 251 | 252 | /** 253 | * Consume link references 254 | */ 255 | protected function consumeReference($lines, $current) 256 | { 257 | while (isset($lines[$current]) && preg_match('/^ {0,3}\[(.+?)\]:\s*(.+?)(?:\s+[\(\'"](.+?)[\)\'"])?\s*$/', $lines[$current], $matches)) { 258 | $label = strtolower($matches[1]); 259 | 260 | $this->references[$label] = [ 261 | 'url' => $this->replaceEscape($matches[2]), 262 | ]; 263 | if (isset($matches[3])) { 264 | $this->references[$label]['title'] = $matches[3]; 265 | } else { 266 | // title may be on the next line 267 | if (isset($lines[$current + 1]) && preg_match('/^\s+[\(\'"](.+?)[\)\'"]\s*$/', $lines[$current + 1], $matches)) { 268 | $this->references[$label]['title'] = $matches[1]; 269 | $current++; 270 | } 271 | } 272 | $current++; 273 | } 274 | return [false, --$current]; 275 | } 276 | } 277 | -------------------------------------------------------------------------------- /cebe-markdown/inline/StrikeoutTrait.php: -------------------------------------------------------------------------------- 1 | parseInline($matches[1]) 26 | ], 27 | strlen($matches[0]) 28 | ]; 29 | } 30 | return [['text', $markdown[0] . $markdown[1]], 2]; 31 | } 32 | 33 | protected function renderStrike($block) 34 | { 35 | return '' . $this->renderAbsy($block[1]) . ''; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /cebe-markdown/inline/UrlLinkTrait.php: -------------------------------------------------------------------------------- 1 | [^\s()]+)|(?R))*\) 28 | | # else match a link with title 29 | ^(https?|ftp):\/\/(([^\s()]+)|(?R))+(?context) && preg_match($pattern, $markdown, $matches)) { 34 | return [ 35 | ['autoUrl', $matches[0]], 36 | strlen($matches[0]) 37 | ]; 38 | } 39 | return [['text', substr($markdown, 0, 4)], 4]; 40 | } 41 | 42 | protected function renderAutoUrl($block) 43 | { 44 | $href = htmlspecialchars($block[1], ENT_COMPAT | ENT_HTML401, 'UTF-8'); 45 | $text = htmlspecialchars(urldecode($block[1]), ENT_NOQUOTES | ENT_SUBSTITUTE, 'UTF-8'); 46 | return "$text"; 47 | } 48 | } 49 | -------------------------------------------------------------------------------- /metadata.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "Markdown Editor", 3 | "uri": "https://github.com/svivian/q2a-markdown-editor", 4 | "description": "Markdown editor plugin for simple text-based markup", 5 | "version": "2.6.2", 6 | "date": "2017-09-07", 7 | "author": "Scott Vivian", 8 | "author_uri": "http://codelair.com", 9 | "license": "GPLv3", 10 | "update_uri": "https://raw.githubusercontent.com/svivian/q2a-markdown-editor/master/metadata.json", 11 | "min_q2a": "1.6", 12 | "min_php": "5.4" 13 | } 14 | -------------------------------------------------------------------------------- /pagedown/LICENSE.txt: -------------------------------------------------------------------------------- 1 | A javascript port of Markdown, as used on Stack Overflow 2 | and the rest of Stack Exchange network. 3 | 4 | Largely based on showdown.js by John Fraser (Attacklab). 5 | 6 | Original Markdown Copyright (c) 2004-2005 John Gruber 7 | 8 | 9 | 10 | Original Showdown code copyright (c) 2007 John Fraser 11 | 12 | Modifications and bugfixes (c) 2009 Dana Robinson 13 | Modifications and bugfixes (c) 2009-2014 Stack Exchange Inc. 14 | 15 | Permission is hereby granted, free of charge, to any person obtaining a copy 16 | of this software and associated documentation files (the "Software"), to deal 17 | in the Software without restriction, including without limitation the rights 18 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 19 | copies of the Software, and to permit persons to whom the Software is 20 | furnished to do so, subject to the following conditions: 21 | 22 | The above copyright notice and this permission notice shall be included in 23 | all copies or substantial portions of the Software. 24 | 25 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 26 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 27 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 28 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 29 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 30 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 31 | THE SOFTWARE. 32 | 33 | -------------------------------------------------------------------------------- /pagedown/Markdown.Sanitizer.js: -------------------------------------------------------------------------------- 1 | (function () { 2 | var output, Converter; 3 | if (typeof exports === "object" && typeof require === "function") { // we're in a CommonJS (e.g. Node.js) module 4 | output = exports; 5 | Converter = require("./Markdown.Converter").Converter; 6 | } else { 7 | output = window.Markdown; 8 | Converter = output.Converter; 9 | } 10 | 11 | output.getSanitizingConverter = function () { 12 | var converter = new Converter(); 13 | converter.hooks.chain("postConversion", sanitizeHtml); 14 | converter.hooks.chain("postConversion", balanceTags); 15 | return converter; 16 | } 17 | 18 | function sanitizeHtml(html) { 19 | return html.replace(/<[^>]*>?/gi, sanitizeTag); 20 | } 21 | 22 | // (tags that can be opened/closed) | (tags that stand alone) 23 | var basic_tag_whitelist = /^(<\/?(b|blockquote|code|del|dd|dl|dt|em|h1|h2|h3|i|kbd|li|ol(?: start="\d+")?|p|pre|s|sup|sub|strong|strike|ul)>|<(br|hr)\s?\/?>)$/i; 24 | // | 25 | var a_white = /^(]+")?\s?>|<\/a>)$/i; 26 | 27 | // ]*")?(\stitle="[^"<>]*")?\s?\/?>)$/i; 29 | 30 | function sanitizeTag(tag) { 31 | if (tag.match(basic_tag_whitelist) || tag.match(a_white) || tag.match(img_white)) 32 | return tag; 33 | else 34 | return ""; 35 | } 36 | 37 | /// 38 | /// attempt to balance HTML tags in the html string 39 | /// by removing any unmatched opening or closing tags 40 | /// IMPORTANT: we *assume* HTML has *already* been 41 | /// sanitized and is safe/sane before balancing! 42 | /// 43 | /// adapted from CODESNIPPET: A8591DBA-D1D3-11DE-947C-BA5556D89593 44 | /// 45 | function balanceTags(html) { 46 | 47 | if (html == "") 48 | return ""; 49 | 50 | var re = /<\/?\w+[^>]*(\s|$|>)/g; 51 | // convert everything to lower case; this makes 52 | // our case insensitive comparisons easier 53 | var tags = html.toLowerCase().match(re); 54 | 55 | // no HTML tags present? nothing to do; exit now 56 | var tagcount = (tags || []).length; 57 | if (tagcount == 0) 58 | return html; 59 | 60 | var tagname, tag; 61 | var ignoredtags = "



  • "; 62 | var match; 63 | var tagpaired = []; 64 | var tagremove = []; 65 | var needsRemoval = false; 66 | 67 | // loop through matched tags in forward order 68 | for (var ctag = 0; ctag < tagcount; ctag++) { 69 | tagname = tags[ctag].replace(/<\/?(\w+).*/, "$1"); 70 | // skip any already paired tags 71 | // and skip tags in our ignore list; assume they're self-closed 72 | if (tagpaired[ctag] || ignoredtags.search("<" + tagname + ">") > -1) 73 | continue; 74 | 75 | tag = tags[ctag]; 76 | match = -1; 77 | 78 | if (!/^<\//.test(tag)) { 79 | // this is an opening tag 80 | // search forwards (next tags), look for closing tags 81 | for (var ntag = ctag + 1; ntag < tagcount; ntag++) { 82 | if (!tagpaired[ntag] && tags[ntag] == "") { 83 | match = ntag; 84 | break; 85 | } 86 | } 87 | } 88 | 89 | if (match == -1) 90 | needsRemoval = tagremove[ctag] = true; // mark for removal 91 | else 92 | tagpaired[match] = true; // mark paired 93 | } 94 | 95 | if (!needsRemoval) 96 | return html; 97 | 98 | // delete all orphaned tags from the string 99 | 100 | var ctag = 0; 101 | html = html.replace(re, function (match) { 102 | var res = tagremove[ctag] ? "" : match; 103 | ctag++; 104 | return res; 105 | }); 106 | return html; 107 | } 108 | })(); 109 | -------------------------------------------------------------------------------- /pagedown/highlight.min.js: -------------------------------------------------------------------------------- 1 | var hljs=new function(){function m(p){return p.replace(/&/gm,"&").replace(/"}while(y.length||w.length){var v=u().splice(0,1)[0];z+=m(x.substr(q,v.offset-q));q=v.offset;if(v.event=="start"){z+=t(v.node);s.push(v.node)}else{if(v.event=="stop"){var p,r=s.length;do{r--;p=s[r];z+=("")}while(p!=v.node);s.splice(r,1);while(r'+N[0]+""}else{r+=N[0]}P=Q.lR.lastIndex;N=Q.lR.exec(M)}return r+M.substr(P)}function B(M,N){var r;if(N.sL==""){r=g(M)}else{r=d(N.sL,M)}if(N.r>0){y+=r.keyword_count;C+=r.r}return''+r.value+""}function K(r,M){if(M.sL&&e[M.sL]||M.sL==""){return B(r,M)}else{return G(r,M)}}function J(N,r){var M=N.cN?'':"";if(N.rB){z+=M;N.buffer=""}else{if(N.eB){z+=m(r)+M;N.buffer=""}else{z+=M;N.buffer=r}}p.push(N);C+=N.r}function H(O,N,R){var S=p[p.length-1];if(R){z+=K(S.buffer+O,S);return false}var Q=s(N,S);if(Q){z+=K(S.buffer+O,S);J(Q,N);return Q.rB}var M=w(p.length-1,N);if(M){var P=S.cN?"":"";if(S.rE){z+=K(S.buffer+O,S)+P}else{if(S.eE){z+=K(S.buffer+O,S)+P+m(N)}else{z+=K(S.buffer+O+N,S)+P}}while(M>1){P=p[p.length-2].cN?"":"";z+=P;M--;p.length--}var r=p[p.length-1];p.length--;p[p.length-1].buffer="";if(r.starts){J(r.starts,"")}return S.rE}if(x(N,S)){throw"Illegal"}}var F=e[D];var p=[F.dM];var C=0;var y=0;var z="";try{var t,v=0;F.dM.buffer="";do{t=q(E,v);var u=H(t[0],t[1],t[2]);v+=t[0].length;if(!u){v+=t[1].length}}while(!t[2]);return{r:C,keyword_count:y,value:z,language:D}}catch(I){if(I=="Illegal"){return{r:0,keyword_count:0,value:m(E)}}else{throw I}}}function g(t){var p={keyword_count:0,r:0,value:m(t)};var r=p;for(var q in e){if(!e.hasOwnProperty(q)){continue}var s=d(q,t);s.language=q;if(s.keyword_count+s.r>r.keyword_count+r.r){r=s}if(s.keyword_count+s.r>p.keyword_count+p.r){r=p;p=s}}if(r.language){p.second_best=r}return p}function i(r,q,p){if(q){r=r.replace(/^((<[^>]+>|\t)+)/gm,function(t,w,v,u){return w.replace(/\t/g,q)})}if(p){r=r.replace(/\n/g,"
    ")}return r}function n(t,w,r){var x=h(t,r);var v=a(t);var y,s;if(v=="no-highlight"){return}if(v){y=d(v,x)}else{y=g(x);v=y.language}var q=c(t);if(q.length){s=document.createElement("pre");s.innerHTML=y.value;y.value=k(q,c(s),x)}y.value=i(y.value,w,r);var u=t.className;if(!u.match("(\\s|^)(language-)?"+v+"(\\s|$)")){u=u?(u+" "+v):v}if(/MSIE [678]/.test(navigator.userAgent)&&t.tagName=="CODE"&&t.parentNode.tagName=="PRE"){s=t.parentNode;var p=document.createElement("div");p.innerHTML="
    "+y.value+"
    ";t=p.firstChild.firstChild;p.firstChild.cN=s.cN;s.parentNode.replaceChild(p.firstChild,s)}else{t.innerHTML=y.value}t.className=u;t.result={language:v,kw:y.keyword_count,re:y.r};if(y.second_best){t.second_best={language:y.second_best.language,kw:y.second_best.keyword_count,re:y.second_best.r}}}function o(){if(o.called){return}o.called=true;var r=document.getElementsByTagName("pre");for(var p=0;p|>=|>>|>>=|>>>|>>>=|\\?|\\[|\\{|\\(|\\^|\\^=|\\||\\|=|\\|\\||~";this.BE={b:"\\\\.",r:0};this.ASM={cN:"string",b:"'",e:"'",i:"\\n",c:[this.BE],r:0};this.QSM={cN:"string",b:'"',e:'"',i:"\\n",c:[this.BE],r:0};this.CLCM={cN:"comment",b:"//",e:"$"};this.CBLCLM={cN:"comment",b:"/\\*",e:"\\*/"};this.HCM={cN:"comment",b:"#",e:"$"};this.NM={cN:"number",b:this.NR,r:0};this.CNM={cN:"number",b:this.CNR,r:0};this.BNM={cN:"number",b:this.BNR,r:0};this.inherit=function(r,s){var p={};for(var q in r){p[q]=r[q]}if(s){for(var q in s){p[q]=s[q]}}return p}}();hljs.LANGUAGES.bash=function(a){var f="true false";var c={cN:"variable",b:"\\$([a-zA-Z0-9_]+)\\b"};var b={cN:"variable",b:"\\$\\{(([^}])|(\\\\}))+\\}",c:[a.CNM]};var g={cN:"string",b:'"',e:'"',i:"\\n",c:[a.BE,c,b],r:0};var d={cN:"string",b:"'",e:"'",c:[{b:"''"}],r:0};var e={cN:"test_condition",b:"",e:"",c:[g,d,c,b,a.CNM],k:{literal:f},r:0};return{dM:{k:{keyword:"if then else fi for break continue while in do done echo exit return set declare",literal:f},c:[{cN:"shebang",b:"(#!\\/bin\\/bash)|(#!\\/bin\\/sh)",r:10},c,b,a.HCM,a.CNM,g,d,a.inherit(e,{b:"\\[ ",e:" \\]",r:0}),a.inherit(e,{b:"\\[\\[ ",e:" \\]\\]"})]}}}(hljs);hljs.LANGUAGES.cs=function(a){return{dM:{k:"abstract as base bool break byte case catch char checked class const continue decimal default delegate do double else enum event explicit extern false finally fixed float for foreach goto if implicit in int interface internal is lock long namespace new null object operator out override params private protected public readonly ref return sbyte sealed short sizeof stackalloc static string struct switch this throw true try typeof uint ulong unchecked unsafe ushort using virtual volatile void while ascending descending from get group into join let orderby partial select set value var where yield",c:[{cN:"comment",b:"///",e:"$",rB:true,c:[{cN:"xmlDocTag",b:"///|"},{cN:"xmlDocTag",b:""}]},a.CLCM,a.CBLCLM,{cN:"preprocessor",b:"#",e:"$",k:"if else elif endif define undef warning error line region endregion pragma checksum"},{cN:"string",b:'@"',e:'"',c:[{b:'""'}]},a.ASM,a.QSM,a.CNM]}}}(hljs);hljs.LANGUAGES.ruby=function(e){var a="[a-zA-Z_][a-zA-Z0-9_]*(\\!|\\?)?";var k="[a-zA-Z_]\\w*[!?=]?|[-+~]\\@|<<|>>|=~|===?|<=>|[<>]=?|\\*\\*|[-/+%^&*~`|]|\\[\\]=?";var g={keyword:"and false then defined module in return redo if BEGIN retry end for true self when next until do begin unless END rescue nil else break undef not super class case require yield alias while ensure elsif or def",keymethods:"__id__ __send__ abort abs all? allocate ancestors any? arity assoc at at_exit autoload autoload? between? binding binmode block_given? call callcc caller capitalize capitalize! casecmp catch ceil center chomp chomp! chop chop! chr class class_eval class_variable_defined? class_variables clear clone close close_read close_write closed? coerce collect collect! compact compact! concat const_defined? const_get const_missing const_set constants count crypt default default_proc delete delete! delete_at delete_if detect display div divmod downcase downcase! downto dump dup each each_byte each_index each_key each_line each_pair each_value each_with_index empty? entries eof eof? eql? equal? eval exec exit exit! extend fail fcntl fetch fileno fill find find_all first flatten flatten! floor flush for_fd foreach fork format freeze frozen? fsync getc gets global_variables grep gsub gsub! has_key? has_value? hash hex id include include? included_modules index indexes indices induced_from inject insert inspect instance_eval instance_method instance_methods instance_of? instance_variable_defined? instance_variable_get instance_variable_set instance_variables integer? intern invert ioctl is_a? isatty iterator? join key? keys kind_of? lambda last length lineno ljust load local_variables loop lstrip lstrip! map map! match max member? merge merge! method method_defined? method_missing methods min module_eval modulo name nesting new next next! nil? nitems nonzero? object_id oct open pack partition pid pipe pop popen pos prec prec_f prec_i print printf private_class_method private_instance_methods private_method_defined? private_methods proc protected_instance_methods protected_method_defined? protected_methods public_class_method public_instance_methods public_method_defined? public_methods push putc puts quo raise rand rassoc read read_nonblock readchar readline readlines readpartial rehash reject reject! remainder reopen replace require respond_to? reverse reverse! reverse_each rewind rindex rjust round rstrip rstrip! scan seek select send set_trace_func shift singleton_method_added singleton_methods size sleep slice slice! sort sort! sort_by split sprintf squeeze squeeze! srand stat step store strip strip! sub sub! succ succ! sum superclass swapcase swapcase! sync syscall sysopen sysread sysseek system syswrite taint tainted? tell test throw times to_a to_ary to_f to_hash to_i to_int to_io to_proc to_s to_str to_sym tr tr! tr_s tr_s! trace_var transpose trap truncate tty? type ungetc uniq uniq! unpack unshift untaint untrace_var upcase upcase! update upto value? values values_at warn write write_nonblock zero? zip"};var c={cN:"yardoctag",b:"@[A-Za-z]+"};var l=[{cN:"comment",b:"#",e:"$",c:[c]},{cN:"comment",b:"^\\=begin",e:"^\\=end",c:[c],r:10},{cN:"comment",b:"^__END__",e:"\\n$"}];var d={cN:"subst",b:"#\\{",e:"}",l:a,k:g};var j=[e.BE,d];var b=[{cN:"string",b:"'",e:"'",c:j,r:0},{cN:"string",b:'"',e:'"',c:j,r:0},{cN:"string",b:"%[qw]?\\(",e:"\\)",c:j},{cN:"string",b:"%[qw]?\\[",e:"\\]",c:j},{cN:"string",b:"%[qw]?{",e:"}",c:j},{cN:"string",b:"%[qw]?<",e:">",c:j,r:10},{cN:"string",b:"%[qw]?/",e:"/",c:j,r:10},{cN:"string",b:"%[qw]?%",e:"%",c:j,r:10},{cN:"string",b:"%[qw]?-",e:"-",c:j,r:10},{cN:"string",b:"%[qw]?\\|",e:"\\|",c:j,r:10}];var i={cN:"function",b:"\\bdef\\s+",e:" |$|;",l:a,k:g,c:[{cN:"title",b:k,l:a,k:g},{cN:"params",b:"\\(",e:"\\)",l:a,k:g}].concat(l)};var h={cN:"identifier",b:a,l:a,k:g,r:0};var f=l.concat(b.concat([{cN:"class",bWK:true,e:"$|;",k:"class module",c:[{cN:"title",b:"[A-Za-z_]\\w*(::\\w+)*(\\?|\\!)?",r:0},{cN:"inheritance",b:"<\\s*",c:[{cN:"parent",b:"("+e.IR+"::)?"+e.IR}]}].concat(l)},i,{cN:"constant",b:"(::)?([A-Z]\\w*(::)?)+",r:0},{cN:"symbol",b:":",c:b.concat([h]),r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{cN:"number",b:"\\?\\w"},{cN:"variable",b:"(\\$\\W)|((\\$|\\@\\@?)(\\w+))"},h,{b:"("+e.RSR+")\\s*",c:l.concat([{cN:"regexp",b:"/",e:"/[a-z]*",i:"\\n",c:[e.BE]}]),r:0}]));d.c=f;i.c[1].c=f;return{dM:{l:a,k:g,c:f}}}(hljs);hljs.LANGUAGES.diff=function(a){return{cI:true,dM:{c:[{cN:"chunk",b:"^\\@\\@ +\\-\\d+,\\d+ +\\+\\d+,\\d+ +\\@\\@$",r:10},{cN:"chunk",b:"^\\*\\*\\* +\\d+,\\d+ +\\*\\*\\*\\*$",r:10},{cN:"chunk",b:"^\\-\\-\\- +\\d+,\\d+ +\\-\\-\\-\\-$",r:10},{cN:"header",b:"Index: ",e:"$"},{cN:"header",b:"=====",e:"=====$"},{cN:"header",b:"^\\-\\-\\-",e:"$"},{cN:"header",b:"^\\*{3} ",e:"$"},{cN:"header",b:"^\\+\\+\\+",e:"$"},{cN:"header",b:"\\*{5}",e:"\\*{5}$"},{cN:"addition",b:"^\\+",e:"$"},{cN:"deletion",b:"^\\-",e:"$"},{cN:"change",b:"^\\!",e:"$"}]}}}(hljs);hljs.LANGUAGES.javascript=function(a){return{dM:{k:{keyword:"in if for while finally var new function do return void else break catch instanceof with throw case default try this switch continue typeof delete",literal:"true false null undefined NaN Infinity"},c:[a.ASM,a.QSM,a.CLCM,a.CBLCLM,a.CNM,{b:"("+a.RSR+"|\\b(case|return|throw)\\b)\\s*",k:"return throw case",c:[a.CLCM,a.CBLCLM,{cN:"regexp",b:"/",e:"/[gim]*",c:[{b:"\\\\/"}]}],r:0},{cN:"function",bWK:true,e:"{",k:"function",c:[{cN:"title",b:"[A-Za-z$_][0-9A-Za-z$_]*"},{cN:"params",b:"\\(",e:"\\)",c:[a.CLCM,a.CBLCLM],i:"[\"'\\(]"}],i:"\\[|%"}]}}}(hljs);hljs.LANGUAGES.css=function(a){var b={cN:"function",b:a.IR+"\\(",e:"\\)",c:[{eW:true,eE:true,c:[a.NM,a.ASM,a.QSM]}]};return{cI:true,dM:{i:"[=/|']",c:[a.CBLCLM,{cN:"id",b:"\\#[A-Za-z0-9_-]+"},{cN:"class",b:"\\.[A-Za-z0-9_-]+",r:0},{cN:"attr_selector",b:"\\[",e:"\\]",i:"$"},{cN:"pseudo",b:":(:)?[a-zA-Z0-9\\_\\-\\+\\(\\)\\\"\\']+"},{cN:"at_rule",b:"@(font-face|page)",l:"[a-z-]+",k:"font-face page"},{cN:"at_rule",b:"@",e:"[{;]",eE:true,k:"import page media charset",c:[b,a.ASM,a.QSM,a.NM]},{cN:"tag",b:a.IR,r:0},{cN:"rules",b:"{",e:"}",i:"[^\\s]",r:0,c:[a.CBLCLM,{cN:"rule",b:"[^\\s]",rB:true,e:";",eW:true,c:[{cN:"attribute",b:"[A-Z\\_\\.\\-]+",e:":",eE:true,i:"[^\\s]",starts:{cN:"value",eW:true,eE:true,c:[b,a.NM,a.QSM,a.ASM,a.CBLCLM,{cN:"hexcolor",b:"\\#[0-9A-F]+"},{cN:"important",b:"!important"}]}}]}]}]}}}(hljs);hljs.LANGUAGES.xml=function(a){var c="[A-Za-z0-9\\._:-]+";var b={eW:true,c:[{cN:"attribute",b:c,r:0},{b:'="',rB:true,e:'"',c:[{cN:"value",b:'"',eW:true}]},{b:"='",rB:true,e:"'",c:[{cN:"value",b:"'",eW:true}]},{b:"=",c:[{cN:"value",b:"[^\\s/>]+"}]}]};return{cI:true,dM:{c:[{cN:"pi",b:"<\\?",e:"\\?>",r:10},{cN:"doctype",b:"",r:10,c:[{b:"\\[",e:"\\]"}]},{cN:"comment",b:"",r:10},{cN:"cdata",b:"<\\!\\[CDATA\\[",e:"\\]\\]>",r:10},{cN:"tag",b:"|$)",e:">",k:{title:"style"},c:[b],starts:{e:"",rE:true,sL:"css"}},{cN:"tag",b:"|$)",e:">",k:{title:"script"},c:[b],starts:{e:"<\/script>",rE:true,sL:"javascript"}},{b:"<%",e:"%>",sL:"vbscript"},{cN:"tag",b:"",c:[{cN:"title",b:"[^ />]+"},b]}]}}}(hljs);hljs.LANGUAGES.http=function(a){return{dM:{i:"\\S",c:[{cN:"status",b:"^HTTP/[0-9\\.]+",e:"$",c:[{cN:"number",b:"\\b\\d{3}\\b"}]},{cN:"request",b:"^[A-Z]+ (.*?) HTTP/[0-9\\.]+$",rB:true,e:"$",c:[{cN:"string",b:" ",e:" ",eB:true,eE:true}]},{cN:"attribute",b:"^\\w",e:": ",eE:true,i:"\\n",starts:{cN:"string",e:"$"}},{b:"\\n\\n",starts:{sL:"",eW:true}}]}}}(hljs);hljs.LANGUAGES.java=function(a){return{dM:{k:"false synchronized int abstract float private char boolean static null if const for true while long throw strictfp finally protected import native final return void enum else break transient new catch instanceof byte super volatile case assert short package default double public try this switch continue throws",c:[{cN:"javadoc",b:"/\\*\\*",e:"\\*/",c:[{cN:"javadoctag",b:"@[A-Za-z]+"}],r:10},a.CLCM,a.CBLCLM,a.ASM,a.QSM,{cN:"class",bWK:true,e:"{",k:"class interface",i:":",c:[{bWK:true,k:"extends implements",r:10},{cN:"title",b:a.UIR}]},a.CNM,{cN:"annotation",b:"@[A-Za-z]+"}]}}}(hljs);hljs.LANGUAGES.php=function(a){var e={cN:"variable",b:"\\$+[a-zA-Z_\x7f-\xff][a-zA-Z0-9_\x7f-\xff]*"};var b=[a.inherit(a.ASM,{i:null}),a.inherit(a.QSM,{i:null}),{cN:"string",b:'b"',e:'"',c:[a.BE]},{cN:"string",b:"b'",e:"'",c:[a.BE]}];var c=[a.CNM,a.BNM];var d={cN:"title",b:a.UIR};return{cI:true,dM:{k:"and include_once list abstract global private echo interface as static endswitch array null if endwhile or const for endforeach self var while isset public protected exit foreach throw elseif include __FILE__ empty require_once do xor return implements parent clone use __CLASS__ __LINE__ else break print eval new catch __METHOD__ case exception php_user_filter default die require __FUNCTION__ enddeclare final try this switch continue endfor endif declare unset true false namespace trait goto instanceof insteadof __DIR__ __NAMESPACE__ __halt_compiler",c:[a.CLCM,a.HCM,{cN:"comment",b:"/\\*",e:"\\*/",c:[{cN:"phpdoc",b:"\\s@[A-Za-z]+"}]},{cN:"comment",eB:true,b:"__halt_compiler.+?;",eW:true},{cN:"string",b:"<<<['\"]?\\w+['\"]?$",e:"^\\w+;",c:[a.BE]},{cN:"preprocessor",b:"<\\?php",r:10},{cN:"preprocessor",b:"\\?>"},e,{cN:"function",bWK:true,e:"{",k:"function",i:"\\$|\\[|%",c:[d,{cN:"params",b:"\\(",e:"\\)",c:["self",e,a.CBLCLM].concat(b).concat(c)}]},{cN:"class",bWK:true,e:"{",k:"class",i:"[:\\(\\$]",c:[{bWK:true,eW:true,k:"extends",c:[d]},d]},{b:"=>"}].concat(b).concat(c)}}}(hljs);hljs.LANGUAGES.python=function(a){var c=[{cN:"string",b:"(u|b)?r?'''",e:"'''",r:10},{cN:"string",b:'(u|b)?r?"""',e:'"""',r:10},{cN:"string",b:"(u|r|ur)'",e:"'",c:[a.BE],r:10},{cN:"string",b:'(u|r|ur)"',e:'"',c:[a.BE],r:10},{cN:"string",b:"(b|br)'",e:"'",c:[a.BE]},{cN:"string",b:'(b|br)"',e:'"',c:[a.BE]}].concat([a.ASM,a.QSM]);var e={cN:"title",b:a.UIR};var d={cN:"params",b:"\\(",e:"\\)",c:["self",a.CNM].concat(c)};var b={bWK:true,e:":",i:"[${=;\\n]",c:[e,d],r:10};return{dM:{k:{keyword:"and elif is global as in if from raise for except finally print import pass return exec else break not with class assert yield try while continue del or def lambda nonlocal|10",built_in:"None True False Ellipsis NotImplemented"},i:"(|\\?)",c:c.concat([a.HCM,a.inherit(b,{cN:"function",k:"def"}),a.inherit(b,{cN:"class",k:"class"}),a.CNM,{cN:"decorator",b:"@",e:"$"},{b:"\\b(print|exec)\\("}])}}}(hljs);hljs.LANGUAGES.sql=function(a){return{cI:true,dM:{i:"[^\\s]",c:[{cN:"operator",b:"(begin|start|commit|rollback|savepoint|lock|alter|create|drop|rename|call|delete|do|handler|insert|load|replace|select|truncate|update|set|show|pragma|grant)\\b",e:";",eW:true,k:{keyword:"all partial global month current_timestamp using go revoke smallint indicator end-exec disconnect zone with character assertion to add current_user usage input local alter match collate real then rollback get read timestamp session_user not integer bit unique day minute desc insert execute like ilike|2 level decimal drop continue isolation found where constraints domain right national some module transaction relative second connect escape close system_user for deferred section cast current sqlstate allocate intersect deallocate numeric public preserve full goto initially asc no key output collation group by union session both last language constraint column of space foreign deferrable prior connection unknown action commit view or first into float year primary cascaded except restrict set references names table outer open select size are rows from prepare distinct leading create only next inner authorization schema corresponding option declare precision immediate else timezone_minute external varying translation true case exception join hour default double scroll value cursor descriptor values dec fetch procedure delete and false int is describe char as at in varchar null trailing any absolute current_time end grant privileges when cross check write current_date pad begin temporary exec time update catalog user sql date on identity timezone_hour natural whenever interval work order cascade diagnostics nchar having left call do handler load replace truncate start lock show pragma",aggregate:"count sum min max avg"},c:[{cN:"string",b:"'",e:"'",c:[a.BE,{b:"''"}],r:0},{cN:"string",b:'"',e:'"',c:[a.BE,{b:'""'}],r:0},{cN:"string",b:"`",e:"`",c:[a.BE]},a.CNM]},a.CBLCLM,{cN:"comment",b:"--",e:"$"}]}}}(hljs);hljs.LANGUAGES.ini=function(a){return{cI:true,dM:{i:"[^\\s]",c:[{cN:"comment",b:";",e:"$"},{cN:"title",b:"^\\[",e:"\\]"},{cN:"setting",b:"^[a-z0-9_\\[\\]]+[ \\t]*=[ \\t]*",e:"$",c:[{cN:"value",eW:true,k:"on off true false yes no",c:[a.QSM,a.NM]}]}]}}}(hljs);hljs.LANGUAGES.perl=function(e){var a="getpwent getservent quotemeta msgrcv scalar kill dbmclose undef lc ma syswrite tr send umask sysopen shmwrite vec qx utime local oct semctl localtime readpipe do return format read sprintf dbmopen pop getpgrp not getpwnam rewinddir qqfileno qw endprotoent wait sethostent bless s|0 opendir continue each sleep endgrent shutdown dump chomp connect getsockname die socketpair close flock exists index shmgetsub for endpwent redo lstat msgctl setpgrp abs exit select print ref gethostbyaddr unshift fcntl syscall goto getnetbyaddr join gmtime symlink semget splice x|0 getpeername recv log setsockopt cos last reverse gethostbyname getgrnam study formline endhostent times chop length gethostent getnetent pack getprotoent getservbyname rand mkdir pos chmod y|0 substr endnetent printf next open msgsnd readdir use unlink getsockopt getpriority rindex wantarray hex system getservbyport endservent int chr untie rmdir prototype tell listen fork shmread ucfirst setprotoent else sysseek link getgrgid shmctl waitpid unpack getnetbyname reset chdir grep split require caller lcfirst until warn while values shift telldir getpwuid my getprotobynumber delete and sort uc defined srand accept package seekdir getprotobyname semop our rename seek if q|0 chroot sysread setpwent no crypt getc chown sqrt write setnetent setpriority foreach tie sin msgget map stat getlogin unless elsif truncate exec keys glob tied closedirioctl socket readlink eval xor readline binmode setservent eof ord bind alarm pipe atan2 getgrent exp time push setgrent gt lt or ne m|0";var d={cN:"subst",b:"[$@]\\{",e:"\\}",k:a,r:10};var b={cN:"variable",b:"\\$\\d"};var i={cN:"variable",b:"[\\$\\%\\@\\*](\\^\\w\\b|#\\w+(\\:\\:\\w+)*|[^\\s\\w{]|{\\w+}|\\w+(\\:\\:\\w*)*)"};var f=[e.BE,d,b,i];var h={b:"->",c:[{b:e.IR},{b:"{",e:"}"}]};var g={cN:"comment",b:"^(__END__|__DATA__)",e:"\\n$",r:5};var c=[b,i,e.HCM,g,{cN:"comment",b:"^\\=\\w",e:"\\=cut",eW:true},h,{cN:"string",b:"q[qwxr]?\\s*\\(",e:"\\)",c:f,r:5},{cN:"string",b:"q[qwxr]?\\s*\\[",e:"\\]",c:f,r:5},{cN:"string",b:"q[qwxr]?\\s*\\{",e:"\\}",c:f,r:5},{cN:"string",b:"q[qwxr]?\\s*\\|",e:"\\|",c:f,r:5},{cN:"string",b:"q[qwxr]?\\s*\\<",e:"\\>",c:f,r:5},{cN:"string",b:"qw\\s+q",e:"q",c:f,r:5},{cN:"string",b:"'",e:"'",c:[e.BE],r:0},{cN:"string",b:'"',e:'"',c:f,r:0},{cN:"string",b:"`",e:"`",c:[e.BE]},{cN:"string",b:"{\\w+}",r:0},{cN:"string",b:"-?\\w+\\s*\\=\\>",r:0},{cN:"number",b:"(\\b0[0-7_]+)|(\\b0x[0-9a-fA-F_]+)|(\\b[1-9][0-9_]*(\\.[0-9_]+)?)|[0_]\\b",r:0},{b:"("+e.RSR+"|\\b(split|return|print|reverse|grep)\\b)\\s*",k:"split return print reverse grep",r:0,c:[e.HCM,g,{cN:"regexp",b:"(s|tr|y)/(\\\\.|[^/])*/(\\\\.|[^/])*/[a-z]*",r:10},{cN:"regexp",b:"(m|qr)?/",e:"/[a-z]*",c:[e.BE],r:0}]},{cN:"sub",bWK:true,e:"(\\s*\\(.*?\\))?[;{]",k:"sub",r:5},{cN:"operator",b:"-\\w\\b",r:0}];d.c=c;h.c[1].c=c;return{dM:{k:a,c:c}}}(hljs);hljs.LANGUAGES.json=function(a){var e={literal:"true false null"};var d=[a.QSM,a.CNM];var c={cN:"value",e:",",eW:true,eE:true,c:d,k:e};var b={b:"{",e:"}",c:[{cN:"attribute",b:'\\s*"',e:'"\\s*:\\s*',eB:true,eE:true,c:[a.BE],i:"\\n",starts:c}],i:"\\S"};var f={b:"\\[",e:"\\]",c:[a.inherit(c,{cN:null})],i:"\\S"};d.splice(d.length,0,b,f);return{dM:{c:d,k:e,i:"\\S"}}}(hljs);hljs.LANGUAGES.cpp=function(a){var b={keyword:"false int float while private char catch export virtual operator sizeof dynamic_cast|10 typedef const_cast|10 const struct for static_cast|10 union namespace unsigned long throw volatile static protected bool template mutable if public friend do return goto auto void enum else break new extern using true class asm case typeid short reinterpret_cast|10 default double register explicit signed typename try this switch continue wchar_t inline delete alignof char16_t char32_t constexpr decltype noexcept nullptr static_assert thread_local restrict _Bool complex",built_in:"std string cin cout cerr clog stringstream istringstream ostringstream auto_ptr deque list queue stack vector map set bitset multiset multimap unordered_set unordered_map unordered_multiset unordered_multimap array shared_ptr"};return{dM:{k:b,i:"",k:b,r:10,c:["self"]}]}}}(hljs); -------------------------------------------------------------------------------- /pagedown/highlightjs-run.js: -------------------------------------------------------------------------------- 1 | 2 | $(function() { 3 | $('.wmd-input').keypress(function() { 4 | window.clearTimeout(hljs.Timeout); 5 | hljs.Timeout = window.setTimeout(function() { 6 | hljs.initHighlighting.called = false; 7 | hljs.initHighlighting(); 8 | }, 500); 9 | }); 10 | window.setTimeout(function() { 11 | hljs.initHighlighting.called = false; 12 | hljs.initHighlighting(); 13 | }, 500); 14 | }); 15 | -------------------------------------------------------------------------------- /pagedown/highlightjs.css: -------------------------------------------------------------------------------- 1 | /* HighlightJS styles */ 2 | 3 | pre code, 4 | pre .ruby .subst, 5 | pre .tag .title, 6 | pre .lisp .title, 7 | pre .nginx .title { 8 | color: black; 9 | } 10 | pre .string, 11 | pre .title, 12 | pre .constant, 13 | pre .parent, 14 | pre .tag .value, 15 | pre .rules .value, 16 | pre .rules .value .number, 17 | pre .preprocessor, 18 | pre .ruby .symbol, 19 | pre .ruby .symbol .string, 20 | pre .ruby .symbol .keyword, 21 | pre .ruby .symbol .keymethods, 22 | pre .instancevar, 23 | pre .aggregate, 24 | pre .template_tag, 25 | pre .django .variable, 26 | pre .smalltalk .class, 27 | pre .addition, 28 | pre .flow, 29 | pre .stream, 30 | pre .bash .variable, 31 | pre .apache .tag, 32 | pre .apache .cbracket, 33 | pre .tex .command, 34 | pre .tex .special, 35 | pre .erlang_repl .function_or_atom, 36 | pre .markdown .header { 37 | color: #800; 38 | } 39 | pre .comment, 40 | pre .annotation, 41 | pre .template_comment, 42 | pre .diff .header, 43 | pre .chunk, 44 | pre .markdown .blockquote { 45 | color: #888; 46 | } 47 | pre .number, 48 | pre .date, 49 | pre .regexp, 50 | pre .literal, 51 | pre .smalltalk .symbol, 52 | pre .smalltalk .char, 53 | pre .go .constant, 54 | pre .change, 55 | pre .markdown .bullet, 56 | pre .markdown .link_url { 57 | color: #080; 58 | } 59 | pre .label, 60 | pre .javadoc, 61 | pre .ruby .string, 62 | pre .decorator, 63 | pre .filter .argument, 64 | pre .localvars, 65 | pre .array, 66 | pre .attr_selector, 67 | pre .important, 68 | pre .pseudo, 69 | pre .pi, 70 | pre .doctype, 71 | pre .deletion, 72 | pre .envvar, 73 | pre .shebang, 74 | pre .apache .sqbracket, 75 | pre .nginx .built_in, 76 | pre .tex .formula, 77 | pre .erlang_repl .reserved, 78 | pre .input_number, 79 | pre .markdown .link_label, 80 | pre .vhdl .attribute { 81 | color: #88f; 82 | } 83 | pre .keyword, 84 | pre .id, 85 | pre .phpdoc, 86 | pre .title, 87 | pre .built_in, 88 | pre .aggregate, 89 | pre .css .tag, 90 | pre .javadoctag, 91 | pre .phpdoc, 92 | pre .yardoctag, 93 | pre .smalltalk .class, 94 | pre .winutils, 95 | pre .bash .variable, 96 | pre .apache .tag, 97 | pre .go .typename, 98 | pre .tex .command, 99 | pre .markdown .strong, 100 | pre .request, 101 | pre .status { 102 | font-weight: bold; 103 | } 104 | pre .markdown .emphasis { 105 | font-style: italic; 106 | } 107 | pre .nginx .built_in { 108 | font-weight: normal; 109 | } 110 | pre .coffeescript .javascript, 111 | pre .xml .css, 112 | pre .xml .javascript, 113 | pre .xml .vbscript, 114 | pre .tex .formula { 115 | opacity: 0.5; 116 | } 117 | -------------------------------------------------------------------------------- /pagedown/markdown.min.js: -------------------------------------------------------------------------------- 1 | "use strict" 2 | var Markdown 3 | Markdown="object"==typeof exports&&"function"==typeof require?exports:{},function(){function e(e){return e}function t(e){return!1}function n(){}function r(){}n.prototype={chain:function(t,n){var r=this[t] 4 | if(!r)throw new Error("unknown hook "+t) 5 | this[t]=r===e?n:function(e){var t=Array.prototype.slice.call(arguments,0) 6 | return t[0]=r.apply(null,t),n.apply(null,t)}},set:function(e,t){if(!this[e])throw new Error("unknown hook "+e) 7 | this[e]=t},addNoop:function(t){this[t]=e},addFalse:function(e){this[e]=t}},Markdown.HookCollection=n,r.prototype={set:function(e,t){this["s_"+e]=t},get:function(e){return this["s_"+e]}},Markdown.Converter=function(t){function u(e){return e=e.replace(/^[ ]{0,3}\[([^\[\]]+)\]:[ \t]*\n?[ \t]*?(?=\s|$)[ \t]*\n?[ \t]*((\n*)["(](.+?)[")][ \t]*)?(\n+)/gm,function(e,t,n,r,u,a,o){return t=t.toLowerCase(),D.set(t,$(n)),u?r+o:(a&&K.set(t,a.replace(/"/g,""")),"")})}function a(e){return e=e.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,i),e=e.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,i),e=e.replace(/\n[ ]{0,3}((<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,i),e=e.replace(/\n\n[ ]{0,3}(-]|-[^>])(?:[^-]|-[^-])*)--)>[ \t]*(?=\n{2,}))/g,i),e=e.replace(/(?:\n\n)([ ]{0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,i)}function o(e){return e=e.replace(/(^\n+|\n+$)/g,""),"\n\n~K"+(N.push(e)-1)+"K\n\n"}function i(e,t){return o(t)}function c(e,t,n){e=q.preBlockGamut(e,U),e=m(e) 8 | return e=e.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm,"
    \n"),e=e.replace(/^[ ]{0,2}([ ]?-[ ]?){3,}[ \t]*$/gm,"
    \n"),e=e.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm,"
    \n"),e=b(e),e=w(e),e=C(e),e=q.postBlockGamut(e,U),e=a(e),e=E(e,t,n)}function l(e){return e=q.preSpanGamut(e),e=T(e),e=s(e),e=S(e),e=p(e),e=f(e),e=_(e),e=e.replace(/~P/g,"://"),e=$(e),e=P(e),e=e.replace(/\n/g,"
    \n"),e=q.postSpanGamut(e)}function s(e){return e=e.replace(/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|-]|-[^>])(?:[^-]|-[^-])*)--)>)/gi,function(e){var t=e.replace(/(.)<\/?code>(?=.)/g,"$1`") 9 | return t=A(t,"!"==e.charAt(1)?"\\`*_/":"\\`*_")})}function f(e){return-1===e.indexOf("[")?e:(e=e.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,d),e=e.replace(/(\[((?:\[[^\]]*\]|[^\[\]])*)\]\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,d),e=e.replace(/(\[([^\[\]]+)\])()()()()()/g,d))}function d(e,t,n,r,u,a,o,i){void 0==i&&(i="") 10 | var c=t,l=n.replace(/:\/\//g,"~P"),s=r.toLowerCase(),f=u,d=i 11 | if(""==f)if(""==s&&(s=l.toLowerCase().replace(/ ?\n/g," ")),f="#"+s,void 0!=D.get(s))f=D.get(s),void 0!=K.get(s)&&(d=K.get(s)) 12 | else{if(!(c.search(/\(\s*\)$/m)>-1))return c 13 | f=""}f=B(f) 14 | var p='"}function p(e){return-1===e.indexOf("![")?e:(e=e.replace(/(!\[(.*?)\][ ]?(?:\n[ ]*)?\[(.*?)\])()()()()/g,h),e=e.replace(/(!\[(.*?)\]\s?\([ \t]*()?[ \t]*((['"])(.*?)\6[ \t]*)?\))/g,h))}function g(e){return e.replace(/>/g,">").replace(/"+l(t)+"\n\n"}),e=e.replace(/^(.+)[ \t]*\n-+[ \t]*\n+/gm,function(e,t){return"

    "+l(t)+"

    \n\n"}),e=e.replace(/^(\#{1,5})[ \t]*(.+?)[ \t]*\#*\n+/gm,function(e,t,n){var r=t.length+1 20 | return""+l(n)+"\n\n"})}function b(e,t){e+="~0" 21 | var n=/^(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm 22 | return O?e=e.replace(n,function(e,n,r){var u,a=n,o=r.search(/[*+-]/g)>-1?"ul":"ol" 23 | "ol"===o&&(u=parseInt(r,10)) 24 | var i=v(a,o,t) 25 | i=i.replace(/\s+$/,"") 26 | var c="<"+o 27 | return u&&1!==u&&(c+=' start="'+u+'"'),i=c+">"+i+"\n"}):(n=/(\n\n|^\n?)(([ ]{0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(~0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/g,e=e.replace(n,function(e,t,n,r){var u,a=t,o=n,i=r.search(/[*+-]/g)>-1?"ul":"ol" 28 | "ol"===i&&(u=parseInt(r,10)) 29 | var c=v(o,i),l="<"+i 30 | return u&&1!==u&&(l+=' start="'+u+'"'),c=a+l+">\n"+c+"\n"})),e=e.replace(/~0/,"")}function v(e,t,n){O++,e=e.replace(/\n{2,}$/,"\n"),e+="~0" 31 | var r=G[t],u=new RegExp("(^[ \\t]*)("+r+")[ \\t]+([^\\r]+?(\\n+))(?=(~0|\\1("+r+")[ \\t]+))","gm"),a=!1 32 | return e=e.replace(u,function(e,t,n,r){var u=r,o=/\n\n$/.test(u),i=o||u.search(/\n{2,}/)>-1,l=i||a 33 | return u=c(R(u),!0,!l),a=o,"
  • "+u+"
  • \n"}),e=e.replace(/~0/g,""),O--,e}function w(e){return e+="~0",e=e.replace(/(?:\n\n|^\n?)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=~0))/g,function(e,t,n){var r=t,u=n 34 | return r=k(R(r)),r=I(r),r=r.replace(/^\n+/g,""),r=r.replace(/\n+$/g,""),"\n\n"+(r="
    "+r+"\n
    ")+"\n\n"+u}),e=e.replace(/~0/,"")}function T(e){return e=e.replace(/(^|[^\\`])(`+)(?!`)([^\r]*?[^`])\2(?!`)/gm,function(e,t,n,r,u){var a=r 35 | return a=a.replace(/^([ \t]*)/g,""),a=a.replace(/[ \t]*$/g,""),a=k(a),a=a.replace(/:\/\//g,"~P"),t+""+a+""})}function k(e){return e=e.replace(/&/g,"&"),e=e.replace(//g,">"),e=A(e,"*_{}[]\\",!1)}function y(e){return-1===e.indexOf("*")&&-1===e.indexOf("_")?e:(e=M(e),e=e.replace(/(^|[\W_])(?:(?!\1)|(?=^))(\*|_)\2(?=\S)([^\r]*?\S)\2\2(?!\2)(?=[\W_]|$)/g,"$1$3"),e=e.replace(/(^|[\W_])(?:(?!\1)|(?=^))(\*|_)(?=\S)((?:(?!\2)[^\r])*?\S)\2(?!\2)(?=[\W_]|$)/g,"$1$3"),z(e))}function x(e){return-1===e.indexOf("*")&&-1===e.indexOf("_")?e:(e=M(e),e=e.replace(/(?=[^\r][*_]|[*_])(^|(?=\W__|(?!\*)[\W_]\*\*|\w\*\*\w)[^\r])(\*\*|__)(?!\2)(?=\S)((?:|[^\r]*?(?!\2)[^\r])(?=\S_|\w|\S\*\*(?:[\W_]|$)).)(?=__(?:\W|$)|\*\*(?:[^*]|$))\2/g,"$1$3"),e=e.replace(/(?=[^\r][*_]|[*_])(^|(?=\W_|(?!\*)(?:[\W_]\*|\D\*(?=\w)\D))[^\r])(\*|_)(?!\2\2\2)(?=\S)((?:(?!\2)[^\r])*?(?=[^\s_]_|(?=\w)\D\*\D|[^\s*]\*(?:[\W_]|$)).)(?=_(?:\W|$)|\*(?:[^*]|$))\2/g,"$1$3"),z(e))}function C(e){return e=e.replace(/((^[ \t]*>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(e,t){var n=t 36 | return n=n.replace(/^[ \t]*>[ \t]?/gm,"~0"),n=n.replace(/~0/g,""),n=n.replace(/^[ \t]+$/gm,""),n=c(n),n=n.replace(/(^|\n)/g,"$1 "),n=n.replace(/(\s*
    [^\r]+?<\/pre>)/gm,function(e,t){var n=t
     37 | return n=n.replace(/^  /gm,"~0"),n=n.replace(/~0/g,"")}),o("
    \n"+n+"\n
    ")})}function E(e,t,n){e=e.replace(/^\n+/g,""),e=e.replace(/\n+$/g,"") 38 | for(var r=e.split(/\n{2,}/g),u=[],a=/~K(\d+)K/,o=r.length,i=0;i"),n||(c+="

    "),u.push(c))}if(!t){o=u.length 40 | for(var i=0;i#+-.!])/g,W)}function L(e,t,n,r){if(t)return e 41 | if(")"!==r.charAt(r.length-1))return"<"+n+r+">" 42 | for(var u=r.match(/[()]/g),a=0,o=0;o"+i}function _(e){e=e.replace(Z,L) 47 | var t=function(e,t){return'
    '+q.plainLinkText(t)+""} 48 | return e=e.replace(/<((https?|ftp):[^'">\s]+)>/gi,t)}function H(e){return e=e.replace(/~E(\d+)E/g,function(e,t){var n=parseInt(t) 49 | return String.fromCharCode(n)})}function R(e){return e=e.replace(/^(\t|[ ]{1,4})/gm,"~0"),e=e.replace(/~0/g,"")}function I(e){if(!/\t/.test(e))return e 50 | var t,n=[" "," "," "," "],r=0 51 | return e.replace(/[\n\t]/g,function(e,u){return"\n"===e?(r=u+1,e):(t=(u-r)%4,r=u+1,n[t])})}function B(e){return e=g(e),e=A(e,"*_:()[]")}function A(e,t,n){var r="(["+t.replace(/([\[\]\\])/g,"\\$1")+"])" 52 | n&&(r="\\\\"+r) 53 | var u=new RegExp(r,"g") 54 | return e=e.replace(u,W)}function W(e,t){return"~E"+t.charCodeAt(0)+"E"}var q=this.hooks=new n 55 | q.addNoop("plainLinkText"),q.addNoop("preConversion"),q.addNoop("postNormalization"),q.addNoop("preBlockGamut"),q.addNoop("postBlockGamut"),q.addNoop("preSpanGamut"),q.addNoop("postSpanGamut"),q.addNoop("postConversion") 56 | var D,K,N,O 57 | t=t||{} 58 | var M=e,z=e 59 | t.nonAsciiLetters&&function(){var e="Q".charCodeAt(0),t="A".charCodeAt(0),n="Z".charCodeAt(0),r="a".charCodeAt(0)-n-1 60 | M=function(u){return u.replace(/[Q\u00aa\u00b5\u00ba\u00c0-\u00d6\u00d8-\u00f6\u00f8-\u02c1\u02c6-\u02d1\u02e0-\u02e4\u02ec\u02ee\u0370-\u0374\u0376-\u0377\u037a-\u037d\u0386\u0388-\u038a\u038c\u038e-\u03a1\u03a3-\u03f5\u03f7-\u0481\u048a-\u0523\u0531-\u0556\u0559\u0561-\u0587\u05d0-\u05ea\u05f0-\u05f2\u0621-\u064a\u0660-\u0669\u066e-\u066f\u0671-\u06d3\u06d5\u06e5-\u06e6\u06ee-\u06fc\u06ff\u0710\u0712-\u072f\u074d-\u07a5\u07b1\u07c0-\u07ea\u07f4-\u07f5\u07fa\u0904-\u0939\u093d\u0950\u0958-\u0961\u0966-\u096f\u0971-\u0972\u097b-\u097f\u0985-\u098c\u098f-\u0990\u0993-\u09a8\u09aa-\u09b0\u09b2\u09b6-\u09b9\u09bd\u09ce\u09dc-\u09dd\u09df-\u09e1\u09e6-\u09f1\u0a05-\u0a0a\u0a0f-\u0a10\u0a13-\u0a28\u0a2a-\u0a30\u0a32-\u0a33\u0a35-\u0a36\u0a38-\u0a39\u0a59-\u0a5c\u0a5e\u0a66-\u0a6f\u0a72-\u0a74\u0a85-\u0a8d\u0a8f-\u0a91\u0a93-\u0aa8\u0aaa-\u0ab0\u0ab2-\u0ab3\u0ab5-\u0ab9\u0abd\u0ad0\u0ae0-\u0ae1\u0ae6-\u0aef\u0b05-\u0b0c\u0b0f-\u0b10\u0b13-\u0b28\u0b2a-\u0b30\u0b32-\u0b33\u0b35-\u0b39\u0b3d\u0b5c-\u0b5d\u0b5f-\u0b61\u0b66-\u0b6f\u0b71\u0b83\u0b85-\u0b8a\u0b8e-\u0b90\u0b92-\u0b95\u0b99-\u0b9a\u0b9c\u0b9e-\u0b9f\u0ba3-\u0ba4\u0ba8-\u0baa\u0bae-\u0bb9\u0bd0\u0be6-\u0bef\u0c05-\u0c0c\u0c0e-\u0c10\u0c12-\u0c28\u0c2a-\u0c33\u0c35-\u0c39\u0c3d\u0c58-\u0c59\u0c60-\u0c61\u0c66-\u0c6f\u0c85-\u0c8c\u0c8e-\u0c90\u0c92-\u0ca8\u0caa-\u0cb3\u0cb5-\u0cb9\u0cbd\u0cde\u0ce0-\u0ce1\u0ce6-\u0cef\u0d05-\u0d0c\u0d0e-\u0d10\u0d12-\u0d28\u0d2a-\u0d39\u0d3d\u0d60-\u0d61\u0d66-\u0d6f\u0d7a-\u0d7f\u0d85-\u0d96\u0d9a-\u0db1\u0db3-\u0dbb\u0dbd\u0dc0-\u0dc6\u0e01-\u0e30\u0e32-\u0e33\u0e40-\u0e46\u0e50-\u0e59\u0e81-\u0e82\u0e84\u0e87-\u0e88\u0e8a\u0e8d\u0e94-\u0e97\u0e99-\u0e9f\u0ea1-\u0ea3\u0ea5\u0ea7\u0eaa-\u0eab\u0ead-\u0eb0\u0eb2-\u0eb3\u0ebd\u0ec0-\u0ec4\u0ec6\u0ed0-\u0ed9\u0edc-\u0edd\u0f00\u0f20-\u0f29\u0f40-\u0f47\u0f49-\u0f6c\u0f88-\u0f8b\u1000-\u102a\u103f-\u1049\u1050-\u1055\u105a-\u105d\u1061\u1065-\u1066\u106e-\u1070\u1075-\u1081\u108e\u1090-\u1099\u10a0-\u10c5\u10d0-\u10fa\u10fc\u1100-\u1159\u115f-\u11a2\u11a8-\u11f9\u1200-\u1248\u124a-\u124d\u1250-\u1256\u1258\u125a-\u125d\u1260-\u1288\u128a-\u128d\u1290-\u12b0\u12b2-\u12b5\u12b8-\u12be\u12c0\u12c2-\u12c5\u12c8-\u12d6\u12d8-\u1310\u1312-\u1315\u1318-\u135a\u1380-\u138f\u13a0-\u13f4\u1401-\u166c\u166f-\u1676\u1681-\u169a\u16a0-\u16ea\u1700-\u170c\u170e-\u1711\u1720-\u1731\u1740-\u1751\u1760-\u176c\u176e-\u1770\u1780-\u17b3\u17d7\u17dc\u17e0-\u17e9\u1810-\u1819\u1820-\u1877\u1880-\u18a8\u18aa\u1900-\u191c\u1946-\u196d\u1970-\u1974\u1980-\u19a9\u19c1-\u19c7\u19d0-\u19d9\u1a00-\u1a16\u1b05-\u1b33\u1b45-\u1b4b\u1b50-\u1b59\u1b83-\u1ba0\u1bae-\u1bb9\u1c00-\u1c23\u1c40-\u1c49\u1c4d-\u1c7d\u1d00-\u1dbf\u1e00-\u1f15\u1f18-\u1f1d\u1f20-\u1f45\u1f48-\u1f4d\u1f50-\u1f57\u1f59\u1f5b\u1f5d\u1f5f-\u1f7d\u1f80-\u1fb4\u1fb6-\u1fbc\u1fbe\u1fc2-\u1fc4\u1fc6-\u1fcc\u1fd0-\u1fd3\u1fd6-\u1fdb\u1fe0-\u1fec\u1ff2-\u1ff4\u1ff6-\u1ffc\u203f-\u2040\u2054\u2071\u207f\u2090-\u2094\u2102\u2107\u210a-\u2113\u2115\u2119-\u211d\u2124\u2126\u2128\u212a-\u212d\u212f-\u2139\u213c-\u213f\u2145-\u2149\u214e\u2183-\u2184\u2c00-\u2c2e\u2c30-\u2c5e\u2c60-\u2c6f\u2c71-\u2c7d\u2c80-\u2ce4\u2d00-\u2d25\u2d30-\u2d65\u2d6f\u2d80-\u2d96\u2da0-\u2da6\u2da8-\u2dae\u2db0-\u2db6\u2db8-\u2dbe\u2dc0-\u2dc6\u2dc8-\u2dce\u2dd0-\u2dd6\u2dd8-\u2dde\u2e2f\u3005-\u3006\u3031-\u3035\u303b-\u303c\u3041-\u3096\u309d-\u309f\u30a1-\u30fa\u30fc-\u30ff\u3105-\u312d\u3131-\u318e\u31a0-\u31b7\u31f0-\u31ff\u3400-\u4db5\u4e00-\u9fc3\ua000-\ua48c\ua500-\ua60c\ua610-\ua62b\ua640-\ua65f\ua662-\ua66e\ua67f-\ua697\ua717-\ua71f\ua722-\ua788\ua78b-\ua78c\ua7fb-\ua801\ua803-\ua805\ua807-\ua80a\ua80c-\ua822\ua840-\ua873\ua882-\ua8b3\ua8d0-\ua8d9\ua900-\ua925\ua930-\ua946\uaa00-\uaa28\uaa40-\uaa42\uaa44-\uaa4b\uaa50-\uaa59\uac00-\ud7a3\uf900-\ufa2d\ufa30-\ufa6a\ufa70-\ufad9\ufb00-\ufb06\ufb13-\ufb17\ufb1d\ufb1f-\ufb28\ufb2a-\ufb36\ufb38-\ufb3c\ufb3e\ufb40-\ufb41\ufb43-\ufb44\ufb46-\ufbb1\ufbd3-\ufd3d\ufd50-\ufd8f\ufd92-\ufdc7\ufdf0-\ufdfb\ufe33-\ufe34\ufe4d-\ufe4f\ufe70-\ufe74\ufe76-\ufefc\uff10-\uff19\uff21-\uff3a\uff3f\uff41-\uff5a\uff66-\uffbe\uffc2-\uffc7\uffca-\uffcf\uffd2-\uffd7\uffda-\uffdc]/g,function(u){for(var a,o=u.charCodeAt(0),i="";o>0;)a=o%51+t,a>=e&&a++,a>n&&(a+=r),i=String.fromCharCode(a)+i,o=o/51|0 61 | return"Q"+i+"Q"})},z=function(u){return u.replace(/Q([A-PR-Za-z]{1,3})Q/g,function(u,a){for(var o,i=0,c=0;cn&&(o-=r),o>e&&o--,o-=t,i=51*i+o 62 | return String.fromCharCode(i)})}}() 63 | var P=t.asteriskIntraWordEmphasis?x:y 64 | this.makeHtml=function(e){if(D)throw new Error("Recursive call to converter.makeHtml") 65 | return D=new r,K=new r,N=[],O=0,e=q.preConversion(e),e=e.replace(/~/g,"~T"),e=e.replace(/\$/g,"~D"),e=e.replace(/\r\n/g,"\n"),e=e.replace(/\r/g,"\n"),e="\n\n"+e+"\n\n",e=I(e),e=e.replace(/^[ \t]+$/gm,""),e=q.postNormalization(e),e=a(e),e=u(e),e=c(e),e=H(e),e=e.replace(/~D/g,"$$"),e=e.replace(/~T/g,"~"),e=q.postConversion(e),N=K=D=null,e} 66 | var U=function(e){return c(e)},G={ol:"\\d+[.]",ul:"[*+-]"},Z=new RegExp('(="|<)?\\b(https?|ftp)(://[-A-Z0-9+&@#/%?=~_|[\\]()!:,.;]*[-A-Z0-9+&@#/%=~_|[\\])])(?=$|\\W)',"gi"),X=new RegExp("[-A-Z0-9+&@#/%=~_|[\\])]","i")}}(),function(){function e(){}function t(e){this.buttonBar=f.getElementById("wmd-button-bar"+e),this.preview=f.getElementById("wmd-preview"+e),this.input=f.getElementById("wmd-input"+e)}function n(e,t){var n,u,a,o=this,i=[],l=0,s="none",f=function(e,t){s!=e&&(s=e,t||p()),h.isIE&&"moving"==s?a=null:u=setTimeout(d,1)},d=function(e){a=new r(t,e),u=void 0} 67 | this.setCommandMode=function(){s="command",p(),u=setTimeout(d,0)},this.canUndo=function(){return l>1},this.canRedo=function(){return!!i[l+1]},this.undo=function(){o.canUndo()&&(n?(n.restore(),n=null):(i[l]=new r(t),i[--l].restore(),e&&e())),s="none",t.input.focus(),d()},this.redo=function(){o.canRedo()&&(i[++l].restore(),e&&e()),s="none",t.input.focus(),d()} 68 | var p=function(){var u=a||new r(t) 69 | return!!u&&("moving"==s?void(n||(n=u)):(n&&(i[l-1].text!=n.text&&(i[l++]=n),n=null),i[l++]=u,i[l+1]=null,void(e&&e())))},g=function(e){var t=!1 70 | if((e.ctrlKey||e.metaKey)&&!e.altKey){var n=e.charCode||e.keyCode 71 | switch(String.fromCharCode(n).toLowerCase()){case"y":o.redo(),t=!0 72 | break 73 | case"z":e.shiftKey?o.redo():o.undo(),t=!0}}if(t)return e.preventDefault&&e.preventDefault(),void(window.event&&(window.event.returnValue=!1))},m=function(e){if(!e.ctrlKey&&!e.metaKey){var t=e.keyCode 74 | t>=33&&t<=40||t>=63232&&t<=63235?f("moving"):8==t||46==t||127==t?f("deleting"):13==t?f("newlines"):27==t?f("escape"):(t<16||t>20)&&91!=t&&f("typing")}},b=function(){c.addEvent(t.input,"keypress",function(e){!e.ctrlKey&&!e.metaKey||e.altKey||89!=e.keyCode&&90!=e.keyCode||e.preventDefault()}) 75 | var e=function(){(h.isIE||a&&a.text!=t.input.value)&&void 0==u&&(s="paste",p(),d())} 76 | c.addEvent(t.input,"keydown",g),c.addEvent(t.input,"keydown",m),c.addEvent(t.input,"mousedown",function(){f("moving")}),t.input.onpaste=e,t.input.ondrop=e} 77 | !function(){b(),d(!0),p()}()}function r(t,n){var r=this,u=t.input 78 | this.init=function(){c.isVisible(u)&&(!n&&f.activeElement&&f.activeElement!==u||(this.setInputAreaSelectionStartEnd(),this.scrollTop=u.scrollTop,(!this.text&&u.selectionStart||0===u.selectionStart)&&(this.text=u.value)))},this.setInputAreaSelection=function(){if(c.isVisible(u))if(void 0===u.selectionStart||h.isOpera){if(f.selection){if(f.activeElement&&f.activeElement!==u)return 79 | u.focus() 80 | var e=u.createTextRange() 81 | e.moveStart("character",-u.value.length),e.moveEnd("character",-u.value.length),e.moveEnd("character",r.end),e.moveStart("character",r.start),e.select()}}else u.focus(),u.selectionStart=r.start,u.selectionEnd=r.end,u.scrollTop=r.scrollTop},this.setInputAreaSelectionStartEnd=function(){if(t.ieCachedRange||!u.selectionStart&&0!==u.selectionStart){if(f.selection){r.text=c.fixEolChars(u.value) 82 | var e=t.ieCachedRange||f.selection.createRange(),n=c.fixEolChars(e.text),a=""+n+"" 83 | e.text=a 84 | var o=c.fixEolChars(u.value) 85 | e.moveStart("character",-a.length),e.text=n,r.start=o.indexOf(""),r.end=o.lastIndexOf("")-"".length 86 | var i=r.text.length-c.fixEolChars(u.value).length 87 | if(i){for(e.moveStart("character",-n.length);i--;)n+="\n",r.end+=1 88 | e.text=n}t.ieCachedRange&&(r.scrollTop=t.ieCachedScrollTop),t.ieCachedRange=null,this.setInputAreaSelection()}}else r.start=u.selectionStart,r.end=u.selectionEnd},this.restore=function(){void 0!=r.text&&r.text!=u.value&&(u.value=r.text),this.setInputAreaSelection(),u.scrollTop=r.scrollTop},this.getChunks=function(){var t=new e 89 | return t.before=c.fixEolChars(r.text.substring(0,r.start)),t.startTag="",t.selection=c.fixEolChars(r.text.substring(r.start,r.end)),t.endTag="",t.after=c.fixEolChars(r.text.substring(r.end)),t.scrollTop=r.scrollTop,t},this.setChunks=function(e){e.before=e.before+e.startTag,e.after=e.endTag+e.after,this.start=e.before.length,this.end=e.before.length+e.selection.length,this.text=e.before+e.selection+e.after,this.scrollTop=e.scrollTop},this.init()}function u(e,t,n){var r,u,a,o=function(e,t){c.addEvent(e,"input",t),e.onpaste=t,e.ondrop=t,c.addEvent(e,"keypress",t),c.addEvent(e,"keydown",t)},i=function(){var e=0 90 | return window.innerHeight?e=window.pageYOffset:f.documentElement&&f.documentElement.scrollTop?e=f.documentElement.scrollTop:f.body&&(e=f.body.scrollTop),e},s=function(){if(t.preview){var n=t.input.value 91 | if(!n||n!=a){a=n 92 | var r=(new Date).getTime() 93 | n=e.makeHtml(n) 94 | var o=(new Date).getTime() 95 | u=o-r,k(n)}}},d=function(){r&&(clearTimeout(r),r=void 0) 96 | var e=0 97 | e=u,e>3e3&&(e=3e3),r=setTimeout(s,e)},p=function(e){return e.scrollHeight<=e.clientHeight?1:e.scrollTop/(e.scrollHeight-e.clientHeight)},g=function(){t.preview&&(t.preview.scrollTop=(t.preview.scrollHeight-t.preview.clientHeight)*p(t.preview))} 98 | this.refresh=function(e){e?(a="",s()):d()},this.processingTime=function(){return u} 99 | var m,b=!0,v=function(e){var n=t.preview,r=n.parentNode,u=n.nextSibling 100 | r.removeChild(n),n.innerHTML=e,u?r.insertBefore(n,u):r.appendChild(n)},w=function(e){t.preview.innerHTML=e},T=function(e){if(m)return m(e) 101 | try{w(e),m=w}catch(t){m=v,m(e)}},k=function(e){var r=l.getTop(t.input)-i() 102 | if(t.preview&&(T(e),n()),g(),b)return void(b=!1) 103 | var u=l.getTop(t.input)-i() 104 | h.isIE?setTimeout(function(){window.scrollBy(0,u-r)},0):window.scrollBy(0,u-r)} 105 | !function(){o(t.input,d),s(),t.preview&&(t.preview.scrollTop=0)}()}function a(e,t,n,u,a,o,i){function l(e){if(m.focus(),e.textOp){n&&n.setCommandMode() 106 | var a=new r(t) 107 | if(!a)return 108 | var o=a.getChunks(),i=function(){m.focus(),o&&a.setChunks(o),a.restore(),u.refresh()} 109 | e.textOp(o,i)||i()}e.execute&&e.execute(n)}function s(e,n){var r=e.getElementsByTagName("span")[0] 110 | n?(r.style.backgroundPosition=e.XShift+" 0px",e.onmouseover=function(){r.style.backgroundPosition=this.XShift+" -40px"},e.onmouseout=function(){r.style.backgroundPosition=this.XShift+" 0px"},h.isIE&&(e.onmousedown=function(){f.activeElement&&f.activeElement!==t.input||(t.ieCachedRange=document.selection.createRange(),t.ieCachedScrollTop=t.input.scrollTop)}),e.isHelp||(e.onclick=function(){return this.onmouseout&&this.onmouseout(),l(this),!1})):(r.style.backgroundPosition=e.XShift+" -20px",e.onmouseover=e.onmouseout=e.onclick=function(){})}function d(e){return"string"==typeof e&&(e=a[e]),function(){e.apply(a,arguments)}}function g(){n&&(s(b.undo,n.canUndo()),s(b.redo,n.canRedo()))}var m=t.input,b={} 111 | !function(){var n=t.buttonBar,r=document.createElement("ul") 112 | r.id="wmd-button-row"+e,r.className="wmd-button-row",r=n.appendChild(r) 113 | var u=0,a=function(t,n,a,o){var i=document.createElement("li") 114 | i.className="wmd-button",i.style.left=u+"px",u+=25 115 | var c=document.createElement("span") 116 | return i.id=t+e,i.appendChild(c),i.title=n,i.XShift=a,o&&(i.textOp=o),s(i,!0),r.appendChild(i),i},c=function(t){var n=document.createElement("li") 117 | n.className="wmd-spacer wmd-spacer"+t,n.id="wmd-spacer"+t+e,r.appendChild(n),u+=25} 118 | b.bold=a("wmd-bold-button",i("bold"),"0px",d("doBold")),b.italic=a("wmd-italic-button",i("italic"),"-20px",d("doItalic")),c(1),b.link=a("wmd-link-button",i("link"),"-40px",d(function(e,t){return this.doLinkOrImage(e,t,!1)})),b.quote=a("wmd-quote-button",i("quote"),"-60px",d("doBlockquote")),b.code=a("wmd-code-button",i("code"),"-80px",d("doCode")),b.image=a("wmd-image-button",i("image"),"-100px",d(function(e,t){return this.doLinkOrImage(e,t,!0)})),c(2),b.olist=a("wmd-olist-button",i("olist"),"-120px",d(function(e,t){this.doList(e,t,!0)})),b.ulist=a("wmd-ulist-button",i("ulist"),"-140px",d(function(e,t){this.doList(e,t,!1)})),b.heading=a("wmd-heading-button",i("heading"),"-160px",d("doHeading")),b.hr=a("wmd-hr-button",i("hr"),"-180px",d("doHorizontalRule")),c(3),b.undo=a("wmd-undo-button",i("undo"),"-200px",null),b.undo.execute=function(e){e&&e.undo()} 119 | var l=i(/win/.test(p.platform.toLowerCase())?"redo":"redomac") 120 | if(b.redo=a("wmd-redo-button",l,"-220px",null),b.redo.execute=function(e){e&&e.redo()},o){var f=document.createElement("li"),h=document.createElement("span") 121 | f.appendChild(h),f.className="wmd-button wmd-help-button",f.id="wmd-help-button"+e,f.XShift="-240px",f.isHelp=!0,f.style.right="0px",f.title=i("help"),f.onclick=o.handler,s(f,!0),r.appendChild(f),b.help=f}g()}() 122 | var v="keydown" 123 | h.isOpera&&(v="keypress"),c.addEvent(m,v,function(e){if((e.ctrlKey||e.metaKey)&&!e.altKey&&!e.shiftKey){var t=e.charCode||e.keyCode 124 | switch(String.fromCharCode(t).toLowerCase()){case"b":l(b.bold) 125 | break 126 | case"i":l(b.italic) 127 | break 128 | case"l":l(b.link) 129 | break 130 | case"q":l(b.quote) 131 | break 132 | case"k":l(b.code) 133 | break 134 | case"g":l(b.image) 135 | break 136 | case"o":l(b.olist) 137 | break 138 | case"u":l(b.ulist) 139 | break 140 | case"h":l(b.heading) 141 | break 142 | case"r":l(b.hr) 143 | break 144 | case"y":l(b.redo) 145 | break 146 | case"z":l(e.shiftKey?b.redo:b.undo) 147 | break 148 | default:return}e.preventDefault&&e.preventDefault(),window.event&&(window.event.returnValue=!1)}}),c.addEvent(m,"keyup",function(e){if(e.shiftKey&&!e.ctrlKey&&!e.metaKey){if(13===(e.charCode||e.keyCode)){var t={} 149 | t.textOp=d("doAutoindent"),l(t)}}}),h.isIE&&c.addEvent(m,"keydown",function(e){if(27===e.keyCode)return!1}),this.setUndoRedoButtonStates=g}function o(e,t,n){this.hooks=e,this.getString=t,this.converter=n}function i(e){return e.replace(/^\s*(.*?)(?:\s+"(.+)")?\s*$/,function(e,t,n){var r=!1 150 | return t=t.replace(/%(?:[\da-fA-F]{2})|\?|\+|[^\w\d-.\/[\]]/g,function(e){if(3===e.length&&"%"==e.charAt(0))return e.toUpperCase() 151 | switch(e){case"?":return r=!0,"?" 152 | case"+":if(r)return"%20"}return encodeURI(e)}),n&&(n=n.trim?n.trim():n.replace(/^\s*/,"").replace(/\s*$/,""),n=n.replace(/"/g,"quot;").replace(/\(/g,"(").replace(/\)/g,")").replace(//g,">")),n?t+' "'+n+'"':t})}var c={},l={},s={},f=window.document,d=window.RegExp,p=window.navigator,g={lineLength:72},h={isIE:/msie/.test(p.userAgent.toLowerCase()),isIE_5or6:/msie 6/.test(p.userAgent.toLowerCase())||/msie 5/.test(p.userAgent.toLowerCase()),isOpera:/opera/.test(p.userAgent.toLowerCase())},m={bold:"Strong Ctrl+B",boldexample:"strong text",italic:"Emphasis Ctrl+I",italicexample:"emphasized text",link:"Hyperlink Ctrl+L",linkdescription:"enter link description here",linkdialog:'

    Enter the web address.

    ',quote:"Blockquote
    Ctrl+Q",quoteexample:"Blockquote",code:"Code Sample
     Ctrl+K",codeexample:"enter code here",image:"Image  Ctrl+G",imagedescription:"",imagedialog:'

    '+(image_upload_enabled?"Upload an image or enter its URL.":"Enter the image URL.")+"

    ",olist:"Numbered List
      Ctrl+O",ulist:"Bulleted List
        Ctrl+U",litem:"List item",heading:"Heading

        /

        Ctrl+H",headingexample:"Heading",hr:"Horizontal Rule
        Ctrl+R",undo:"Undo - Ctrl+Z",redo:"Redo - Ctrl+Y",redomac:"Redo - Ctrl+Shift+Z",help:"Markdown Editing Help"} 153 | Markdown.Editor=function(e,r,i){i=i||{},"function"==typeof i.handler&&(i={helpButton:i}),i.strings=i.strings||{},i.helpButton&&(i.strings.help=i.strings.help||i.helpButton.title) 154 | var c=function(e){return i.strings[e]||m[e]} 155 | r=r||"" 156 | var l=this.hooks=new Markdown.HookCollection 157 | l.addNoop("onPreviewRefresh"),l.addNoop("postBlockquoteCreation"),l.addFalse("insertImageDialog"),this.getConverter=function(){return e} 158 | var s,d=this 159 | this.run=function(){if(!s){s=new t(r) 160 | var p,g,h=new o(l,c,e),m=new u(e,s,function(){l.onPreviewRefresh()});/\?noundo/.test(f.location.href)||(p=new n(function(){m.refresh(),g&&g.setUndoRedoButtonStates()},s),this.textOperation=function(e){p.setCommandMode(),e(),d.refreshPreview()}),g=new a(r,s,p,m,h,i.helpButton,c),g.setUndoRedoButtonStates();(d.refreshPreview=function(){m.refresh(!0)})()}}},e.prototype.findTags=function(e,t){var n,r=this 161 | e&&(n=c.extendRegExp(e,"","$"),this.before=this.before.replace(n,function(e){return r.startTag=r.startTag+e,""}),n=c.extendRegExp(e,"^",""),this.selection=this.selection.replace(n,function(e){return r.startTag=r.startTag+e,""})),t&&(n=c.extendRegExp(t,"","$"),this.selection=this.selection.replace(n,function(e){return r.endTag=e+r.endTag,""}),n=c.extendRegExp(t,"^",""),this.after=this.after.replace(n,function(e){return r.endTag=e+r.endTag,""}))},e.prototype.trimWhitespace=function(e){var t,n,r=this 162 | e?t=n="":(t=function(e){return r.before+=e,""},n=function(e){return r.after=e+r.after,""}),this.selection=this.selection.replace(/^(\s*)/,t).replace(/(\s*)$/,n)},e.prototype.skipLines=function(e,t,n){void 0===e&&(e=1),void 0===t&&(t=1),e++,t++ 163 | var r,u 164 | if(navigator.userAgent.match(/Chrome/)&&"X".match(/()./),this.selection=this.selection.replace(/(^\n*)/,""),this.startTag=this.startTag+d.$1,this.selection=this.selection.replace(/(\n*$)/,""),this.endTag=this.endTag+d.$1,this.startTag=this.startTag.replace(/(^\n*)/,""),this.before=this.before+d.$1,this.endTag=this.endTag.replace(/(\n*$)/,""),this.after=this.after+d.$1,this.before){for(r=u="";e--;)r+="\\n?",u+="\n" 165 | n&&(r="\\n*"),this.before=this.before.replace(new d(r+"$",""),u)}if(this.after){for(r=u="";t--;)r+="\\n?",u+="\n" 166 | n&&(r="\\n*"),this.after=this.after.replace(new d(r,""),u)}},c.isVisible=function(e){return window.getComputedStyle?"none"!==window.getComputedStyle(e,null).getPropertyValue("display"):e.currentStyle?"none"!==e.currentStyle.display:void 0},c.addEvent=function(e,t,n){e.attachEvent?e.attachEvent("on"+t,n):e.addEventListener(t,n,!1)},c.removeEvent=function(e,t,n){e.detachEvent?e.detachEvent("on"+t,n):e.removeEventListener(t,n,!1)},c.fixEolChars=function(e){return e=e.replace(/\r\n/g,"\n"),e=e.replace(/\r/g,"\n")},c.extendRegExp=function(e,t,n){null!==t&&void 0!==t||(t=""),null!==n&&void 0!==n||(n="") 167 | var r,u=e.toString() 168 | return u=u.replace(/\/([gim]*)$/,function(e,t){return r=t,""}),u=u.replace(/(^\/|\/$)/g,""),u=t+u+n,new d(u,r)},l.getTop=function(e,t){var n=e.offsetTop 169 | if(!t)for(;e=e.offsetParent;)n+=e.offsetTop 170 | return n},l.getHeight=function(e){return e.offsetHeight||e.scrollHeight},l.getWidth=function(e){return e.offsetWidth||e.scrollWidth},l.getPageSize=function(){var e,t,n,r 171 | return self.innerHeight&&self.scrollMaxY?(e=f.body.scrollWidth,t=self.innerHeight+self.scrollMaxY):f.body.scrollHeight>f.body.offsetHeight?(e=f.body.scrollWidth,t=f.body.scrollHeight):(e=f.body.offsetWidth,t=f.body.offsetHeight),self.innerHeight?(n=self.innerWidth,r=self.innerHeight):f.documentElement&&f.documentElement.clientHeight?(n=f.documentElement.clientWidth,r=f.documentElement.clientHeight):f.body&&(n=f.body.clientWidth,r=f.body.clientHeight),[Math.max(e,n),Math.max(t,r),n,r]},s.createBackground=function(){var e=f.createElement("div"),t=e.style 172 | e.className="wmd-prompt-background",t.position="absolute",t.top="0",t.zIndex="1000",h.isIE?t.filter="alpha(opacity=50)":t.opacity="0.5" 173 | var n=l.getPageSize() 174 | return t.height=n[1]+"px",h.isIE?(t.left=f.documentElement.scrollLeft,t.width=f.documentElement.clientWidth):(t.left="0",t.width="100%"),f.body.appendChild(e),e},s.prompt=function(e,t,n,r){var u,a,o,i,s 175 | void 0===t&&(t="") 176 | var d=function(e){if(27===(e.charCode||e.keyCode))return e.stopPropagation&&e.stopPropagation(),p(!0),!1},p=function(e,t){c.removeEvent(f.body,"keyup",d) 177 | var l=a.value 178 | if(e)l=null 179 | else{if(r&&i.files[0]&&!s)return s=!0,void $.ajax({url:image_upload_path,type:"POST",data:new FormData(t),contentType:!1,cache:!1,processData:!1,success:function(e){s=!1,o.innerHTML=e.error,e.error||(l=e.url,u.parentNode.removeChild(u),n(l))},error:function(e){s=!1,o.innerHTML("There was an error processing your request.")}}) 180 | l=l.replace(/^http:\/\/(https?|ftp):\/\//,"$1://"),/^(?:https?|ftp):\/\//.test(l)||(l="http://"+l)}return u.parentNode.removeChild(u),n(l),!1},g=function(){u=f.createElement("div"),u.className="wmd-prompt-dialog",u.style.padding="10px;",u.style.position="fixed",u.style.width="400px",u.style.zIndex="1001" 181 | var n=f.createElement("div") 182 | n.innerHTML=e,n.style.padding="5px",u.appendChild(n) 183 | var s=f.createElement("form"),g=s.style 184 | s.onsubmit=function(){return p(!1)},g.padding="0",g.margin="0",g.cssFloat="left",g.width="100%",g.textAlign="center",g.position="relative",u.appendChild(s),r&&(i=f.createElement("input"),i.type="file",i.name="image",i.accept="image/x-png,image/gif,image/jpeg",g=i.style,g.display="block",g.width="80%",g.marginLeft=g.marginRight="auto",s.appendChild(i),o=f.createElement("p"),g=o.style,g.clear="both",g.color="red",s.appendChild(o)),a=f.createElement("input"),a.type="text",a.value=t,g=a.style,g.display="block",g.width="80%",g.marginLeft=g.marginRight="auto",s.appendChild(a) 185 | var m=f.createElement("input") 186 | m.type="button",m.onclick=function(){return p(!1,s)},m.value="OK",g=m.style,g.margin="10px",g.display="inline",g.width="7em" 187 | var b=f.createElement("input") 188 | b.type="button",b.onclick=function(){return p(!0)},b.value="Cancel",g=b.style,g.margin="10px",g.display="inline",g.width="7em",s.appendChild(m),s.appendChild(b),c.addEvent(f.body,"keyup",d),u.style.top="50%",u.style.left="50%",u.style.display="block",h.isIE_5or6&&(u.style.position="absolute",u.style.top=f.documentElement.scrollTop+200+"px",u.style.left="50%"),f.body.appendChild(u),u.style.marginTop=-l.getHeight(u)/2+"px",u.style.marginLeft=-l.getWidth(u)/2+"px"} 189 | setTimeout(function(){g() 190 | var e=t.length 191 | if(void 0!==a.selectionStart)a.selectionStart=0,a.selectionEnd=e 192 | else if(a.createTextRange){var n=a.createTextRange() 193 | n.collapse(!1),n.moveStart("character",-e),n.moveEnd("character",e),n.select()}a.focus()},0)} 194 | var b=o.prototype 195 | b.prefixes="(?:\\s{4,}|\\s*>|\\s*-\\s+|\\s*\\d+\\.|=|\\+|-|_|\\*|#|\\s*\\[[^\n]]+\\]:)",b.unwrap=function(e){var t=new d("([^\\n])\\n(?!(\\n|"+this.prefixes+"))","g") 196 | e.selection=e.selection.replace(t,"$1 $2")},b.wrap=function(e,t){this.unwrap(e) 197 | var n=new d("(.{1,"+t+"})( +|$\\n?)","gm"),r=this 198 | e.selection=e.selection.replace(n,function(e,t){return new d("^"+r.prefixes,"").test(e)?e:t+"\n"}),e.selection=e.selection.replace(/\s+$/,"")},b.doBold=function(e,t){return this.doBorI(e,t,2,this.getString("boldexample"))},b.doItalic=function(e,t){return this.doBorI(e,t,1,this.getString("italicexample"))},b.doBorI=function(e,t,n,r){e.trimWhitespace(),e.selection=e.selection.replace(/\n{2,}/g,"\n") 199 | var u=/(\**$)/.exec(e.before)[0],a=/(^\**)/.exec(e.after)[0],o=Math.min(u.length,a.length) 200 | if(o>=n&&(2!=o||1!=n))e.before=e.before.replace(d("[*]{"+n+"}$",""),""),e.after=e.after.replace(d("^[*]{"+n+"}",""),"") 201 | else if(!e.selection&&a){e.after=e.after.replace(/^([*_]*)/,""),e.before=e.before.replace(/(\s?)$/,"") 202 | var i=d.$1 203 | e.before=e.before+a+i}else{e.selection||a||(e.selection=r) 204 | var c=n<=1?"*":"**" 205 | e.before=e.before+c,e.after=c+e.after}},b.stripLinkDefs=function(e,t){return e=e.replace(/^[ ]{0,3}\[(\d+)\]:[ \t]*\n?[ \t]*?[ \t]*\n?[ \t]*(?:(\n*)["(](.+?)[")][ \t]*)?(?:\n+|$)/gm,function(e,n,r,u,a){return t[n]=e.replace(/\s*$/,""),u?(t[n]=e.replace(/["(](.+?)[")]$/,""),u+a):""})},b.addLinkDef=function(e,t){var n=0,r={} 206 | e.before=this.stripLinkDefs(e.before,r),e.selection=this.stripLinkDefs(e.selection,r),e.after=this.stripLinkDefs(e.after,r) 207 | for(var u="",a=/(\[)((?:\[[^\]]*\]|[^\[\]])*)(\][ ]?(?:\n[ ]*)?\[)(\d+)(\])/g,o=e.before+e.selection+e.after,i=this.converter.makeHtml(o),c="http://this-is-a-real-link.biz/";-1!=i.indexOf(c);)c+="nicetry/" 208 | var l="\n\n",s=0,f=o.replace(a,function e(t,n,r,u,o,i,f){s+=f,l+=" ["+s+"]: "+c+s+"/unicorn\n",s+=n.length,r=r.replace(a,e),s-=n.length 209 | var d=n+r+u+s+i 210 | return s-=f,d}) 211 | i=this.converter.makeHtml(f+l) 212 | var d=function(e){return-1!==i.indexOf(c+e+"/unicorn")},p=function(e){n++,e=e.replace(/^[ ]{0,3}\[(\d+)\]:/," ["+n+"]:"),u+="\n"+e},g=function(e,t,u,o,i,c,l){return d(s+l)?(s+=l+t.length,u=u.replace(a,g),s-=l+t.length,r[i]?(p(r[i]),t+u+o+n+c):e):e},h=e.before.length 213 | e.before=e.before.replace(a,g),s+=h,h=e.selection.length,t?p(t):e.selection=e.selection.replace(a,g),s+=h 214 | var m=n 215 | return e.after=e.after.replace(a,g),e.after&&(e.after=e.after.replace(/\n*$/,"")),e.after||(e.selection=e.selection.replace(/\n*$/,"")),e.after+="\n\n"+u,m},b.doLinkOrImage=function(e,t,n){e.trimWhitespace(),e.findTags(/\s*!?\[/,/\][ ]?(?:\n[ ]*)?(\[.*?\])?/) 216 | var r 217 | if(!(e.endTag.length>1&&e.startTag.length>0)){if(e.selection=e.startTag+e.selection+e.endTag,e.startTag=e.endTag="",/\n\n/.test(e.selection))return void this.addLinkDef(e,null) 218 | var u=this,a=function(a){if(r.parentNode.removeChild(r),null!==a){e.selection=(" "+e.selection).replace(/([^\\](?:\\\\)*)(?=[[\]])/g,"$1\\").substr(1) 219 | var o=" [999]: "+i(a),c=u.addLinkDef(e,o) 220 | e.startTag=n?"![":"[",e.endTag="]["+c+"]",e.selection||(e.selection=n?u.getString("imagedescription"):u.getString("linkdescription"))}t()} 221 | return r=s.createBackground(),n?this.hooks.insertImageDialog(a)||s.prompt(this.getString("imagedialog"),"http://",a,image_upload_enabled):s.prompt(this.getString("linkdialog"),"http://",a),!0}e.startTag=e.startTag.replace(/!?\[/,""),e.endTag="",this.addLinkDef(e,null)},b.doAutoindent=function(e,t){var n=this,r=!1 222 | e.before=e.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/,"\n\n"),e.before=e.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/,"\n\n"),e.before=e.before.replace(/(\n|^)[ \t]+\n$/,"\n\n"),e.selection||/^[ \t]*(?:\n|$)/.test(e.after)||(e.after=e.after.replace(/^[^\n]*/,function(t){return e.selection=t,""}),r=!0),/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]+.*\n$/.test(e.before)&&n.doList&&n.doList(e),/(\n|^)[ ]{0,3}>[ \t]+.*\n$/.test(e.before)&&n.doBlockquote&&n.doBlockquote(e),/(\n|^)(\t|[ ]{4,}).*\n$/.test(e.before)&&n.doCode&&n.doCode(e),r&&(e.after=e.selection+e.after,e.selection="")},b.doBlockquote=function(e,t){e.selection=e.selection.replace(/^(\n*)([^\r]+?)(\n*)$/,function(t,n,r,u){return e.before+=n,e.after=u+e.after,r}),e.before=e.before.replace(/(>[ \t]*)$/,function(t,n){return e.selection=n+e.selection,""}),e.selection=e.selection.replace(/^(\s|>)+$/,""),e.selection=e.selection||this.getString("quoteexample") 223 | var n,r="",u="" 224 | if(e.before){for(var a=e.before.replace(/\n$/,"").split("\n"),o=!1,i=0;i0,/^>/.test(n)?(c=!0,!o&&n.length>1&&(o=!0)):c=!!/^[ \t]*$/.test(n)||o,c?r+=n+"\n":(u+=r+n,r="\n")}/(^|\n)>/.test(r)||(u+=r,r="")}e.startTag=r,e.before=u,e.after&&(e.after=e.after.replace(/^\n?/,"\n")),e.after=e.after.replace(/^(((\n|^)(\n[ \t]*)*>(.+\n)*.*)+(\n[ \t]*)*)/,function(t){return e.endTag=t,""}) 226 | var l=function(t){var n=t?"> ":"" 227 | e.startTag&&(e.startTag=e.startTag.replace(/\n((>|\s)*)\n$/,function(e,t){return"\n"+t.replace(/^[ ]{0,3}>?[ \t]*$/gm,n)+"\n"})),e.endTag&&(e.endTag=e.endTag.replace(/^\n((>|\s)*)\n/,function(e,t){return"\n"+t.replace(/^[ ]{0,3}>?[ \t]*$/gm,n)+"\n"}))};/^(?![ ]{0,3}>)/m.test(e.selection)?(this.wrap(e,g.lineLength-2),e.selection=e.selection.replace(/^/gm,"> "),l(!0),e.skipLines()):(e.selection=e.selection.replace(/^[ ]{0,3}> ?/gm,""),this.unwrap(e),l(!1),!/^(\n|^)[ ]{0,3}>/.test(e.selection)&&e.startTag&&(e.startTag=e.startTag.replace(/\n{0,2}$/,"\n\n")),!/(\n|^)[ ]{0,3}>.*$/.test(e.selection)&&e.endTag&&(e.endTag=e.endTag.replace(/^\n{0,2}/,"\n\n"))),e.selection=this.hooks.postBlockquoteCreation(e.selection),/\n/.test(e.selection)||(e.selection=e.selection.replace(/^(> *)/,function(t,n){return e.startTag+=n,""}))},b.doCode=function(e,t){var n=/\S[ ]*$/.test(e.before) 228 | if(!/^[ ]*\S/.test(e.after)&&!n||/\n/.test(e.selection)){e.before=e.before.replace(/[ ]{4}$/,function(t){return e.selection=t+e.selection,""}) 229 | var r=1,u=1;/(\n|^)(\t|[ ]{4,}).*\n$/.test(e.before)&&(r=0),/^\n(\t|[ ]{4,})/.test(e.after)&&(u=0),e.skipLines(r,u),e.selection?/^[ ]{0,3}\S/m.test(e.selection)?/\n/.test(e.selection)?e.selection=e.selection.replace(/^/gm," "):e.before+=" ":e.selection=e.selection.replace(/^(?:[ ]{4}|[ ]{0,3}\t)/gm,""):(e.startTag=" ",e.selection=this.getString("codeexample"))}else e.trimWhitespace(),e.findTags(/`/,/`/),e.startTag||e.endTag?e.endTag&&!e.startTag?(e.before+=e.endTag,e.endTag=""):e.startTag=e.endTag="":(e.startTag=e.endTag="`",e.selection||(e.selection=this.getString("codeexample")))},b.doList=function(e,t,n){var r=/^\n*(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*/,u="-",a=1,o=function(){var e 230 | return n?(e=" "+a+". ",a++):e=" "+u+" ",e},i=function(e){return void 0===n&&(n=/^\s*\d/.test(e)),e=e.replace(/^[ ]{0,3}([*+-]|\d+[.])\s/gm,function(e){return o()})} 231 | if(e.findTags(/(\n|^)*[ ]{0,3}([*+-]|\d+[.])\s+/,null),!e.before||/\n$/.test(e.before)||/^\n/.test(e.startTag)||(e.before+=e.startTag,e.startTag=""),e.startTag){var c=/\d+[.]/.test(e.startTag) 232 | if(e.startTag="",e.selection=e.selection.replace(/\n[ ]{4}/g,"\n"),this.unwrap(e),e.skipLines(),c&&(e.after=e.after.replace(r,i)),n==c)return}var l=1 233 | e.before=e.before.replace(/(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/,function(e){return/^\s*([*+-])/.test(e)&&(u=d.$1),l=/[^\n]\n\n[^\n]/.test(e)?1:0,i(e)}),e.selection||(e.selection=this.getString("litem")) 234 | var s=o(),f=1 235 | e.after=e.after.replace(r,function(e){return f=/[^\n]\n\n[^\n]/.test(e)?1:0,i(e)}),e.trimWhitespace(!0),e.skipLines(l,f,!0),e.startTag=s 236 | var p=s.replace(/./g," ") 237 | this.wrap(e,g.lineLength-p.length),e.selection=e.selection.replace(/\n/g,"\n"+p)},b.doHeading=function(e,t){if(e.selection=e.selection.replace(/\s+/g," "),e.selection=e.selection.replace(/(^\s+|\s+$)/g,""),!e.selection)return e.startTag="## ",e.selection=this.getString("headingexample"),void(e.endTag=" ##") 238 | var n=0 239 | e.findTags(/#+[ ]*/,/[ ]*#+/),/#+/.test(e.startTag)&&(n=d.lastMatch.length),e.startTag=e.endTag="",e.findTags(null,/\s?(-+|=+)/),/=+/.test(e.endTag)&&(n=1),/-+/.test(e.endTag)&&(n=2),e.startTag=e.endTag="",e.skipLines(1,1) 240 | var r=0==n?2:n-1 241 | if(r>0){var u=r>=2?"-":"=",a=e.selection.length 242 | for(a>g.lineLength&&(a=g.lineLength),e.endTag="\n";a--;)e.endTag+=u}},b.doHorizontalRule=function(e,t){e.startTag="----------\n",e.selection="",e.skipLines(2,1,!0)}}(),function(){function e(e){return e.replace(/<[^>]*>?/gi,t)}function t(e){return e.match(a)||e.match(o)||e.match(i)?e:""}function n(e){if(""==e)return"" 243 | var t=/<\/?\w+[^>]*(\s|$|>)/g,n=e.toLowerCase().match(t),r=(n||[]).length 244 | if(0==r)return e 245 | for(var u,a,o,i="



      • ",c=[],l=[],s=!1,f=0;f")>-1)){if(a=n[f],o=-1,!/^<\//.test(a))for(var d=f+1;d"){o=d 246 | break}-1==o?s=l[f]=!0:c[o]=!0}if(!s)return e 247 | var f=0 248 | return e=e.replace(t,function(e){var t=l[f]?"":e 249 | return f++,t})}var r,u 250 | "object"==typeof exports&&"function"==typeof require?(r=exports,u=require("./Markdown.Converter").Converter):(r=window.Markdown,u=r.Converter),r.getSanitizingConverter=function(){var t=new u 251 | return t.hooks.chain("postConversion",e),t.hooks.chain("postConversion",n),t} 252 | var a=/^(<\/?(b|blockquote|code|del|dd|dl|dt|em|h1|h2|h3|i|kbd|li|ol(?: start="\d+")?|p|pre|s|sup|sub|strong|strike|ul)>|<(br|hr)\s?\/?>)$/i,o=/^(]+")?\s?>|<\/a>)$/i,i=/^(]*")?(\stitle="[^"<>]*")?\s?\/?>)$/i}() 253 | -------------------------------------------------------------------------------- /pagedown/wmd-buttons.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/svivian/q2a-markdown-editor/c98a3ddd4ec0f3f0dccbe871a9d2e7ff9d83914e/pagedown/wmd-buttons.png -------------------------------------------------------------------------------- /pagedown/wmd.css: -------------------------------------------------------------------------------- 1 | /* Markdown editor styles */ 2 | 3 | .wmd-button-bar { 4 | width: 100%; 5 | padding: 5px 0; 6 | } 7 | .wmd-input { 8 | /* 604 */ 9 | width: 598px; 10 | height: 250px; 11 | margin: 0 0 10px; 12 | padding: 2px; 13 | border: 1px solid #ccc; 14 | } 15 | .wmd-preview { 16 | /* 604 */ 17 | width: 584px; 18 | margin: 10px 0; 19 | padding: 8px; 20 | border: 2px dashed #ccc; 21 | } 22 | 23 | .wmd-preview img, .entry-content img { 24 | max-width: 100% 25 | } 26 | 27 | .qa-q-view-content pre, 28 | .qa-a-item-content pre, 29 | .wmd-preview pre { 30 | overflow: auto; 31 | width: 100%; 32 | max-height: 400px; 33 | padding: 0; 34 | border-width: 1px 1px 1px 3px; 35 | border-style: solid; 36 | border-color: #ddd; 37 | background-color: #eee; 38 | } 39 | pre code { 40 | display: block; 41 | padding: 8px; 42 | } 43 | 44 | .wmd-button-row { 45 | position: relative; 46 | margin: 0; 47 | padding: 0; 48 | height: 20px; 49 | } 50 | 51 | .wmd-spacer { 52 | width: 1px; 53 | height: 20px; 54 | margin-left: 14px; 55 | position: absolute; 56 | background-color: Silver; 57 | display: inline-block; 58 | list-style: none; 59 | } 60 | 61 | .wmd-button { 62 | width: 20px; 63 | height: 20px; 64 | padding-left: 2px; 65 | padding-right: 3px; 66 | position: absolute; 67 | display: inline-block; 68 | list-style: none; 69 | cursor: pointer; 70 | } 71 | 72 | .wmd-button > span { 73 | /* note: background-image is set in plugin script */ 74 | background-repeat: no-repeat; 75 | background-position: 0px 0px; 76 | width: 20px; 77 | height: 20px; 78 | display: inline-block; 79 | } 80 | 81 | .wmd-spacer1 { 82 | left: 50px; 83 | } 84 | .wmd-spacer2 { 85 | left: 175px; 86 | } 87 | .wmd-spacer3 { 88 | left: 300px; 89 | } 90 | 91 | .wmd-prompt-background { 92 | background-color: #000; 93 | } 94 | .wmd-prompt-dialog { 95 | border: 1px solid #999; 96 | background-color: #f5f5f5; 97 | } 98 | .wmd-prompt-dialog > div { 99 | font-size: 0.8em; 100 | } 101 | .wmd-prompt-dialog > form > input[type="text"] { 102 | border: 1px solid #999; 103 | color: black; 104 | } 105 | .wmd-prompt-dialog > form > input[type="button"] { 106 | border: 1px solid #888; 107 | font-size: 11px; 108 | font-weight: bold; 109 | } 110 | -------------------------------------------------------------------------------- /qa-markdown-editor.php: -------------------------------------------------------------------------------- 1 | pluginurl = $urltoroot; 18 | } 19 | 20 | public function calc_quality($content, $format) 21 | { 22 | return $format == 'markdown' ? 1.0 : 0.8; 23 | } 24 | 25 | public function option_default($opt) 26 | { 27 | $defaults = [ 28 | $this->cssopt => 0, 29 | $this->convopt => 1, 30 | $this->hljsopt => 0, 31 | $this->impuplopt => 0, 32 | ]; 33 | 34 | if (isset($defaults[$opt])) { 35 | return $defaults[$opt]; 36 | } 37 | } 38 | 39 | public function get_field(&$qa_content, $content, $format, $fieldname, $rows, $autofocus) 40 | { 41 | $html = '
        ' . "\n"; 42 | $html .= '' . "\n"; 43 | $html .= '

        '.qa_lang_html('markdown/preview').'

        ' . "\n"; 44 | $html .= '
        ' . "\n"; 45 | 46 | $imageUploadUrl = qa_js(qa_path('qa-markdown-upload')); 47 | $uploadimg = (int) qa_opt($this->impuplopt); 48 | $html .= ""; 49 | 50 | // $html .= '' . "\n"; 51 | // $html .= '' . "\n"; 52 | // $html .= '' . "\n"; 53 | 54 | // comment this script and uncomment the 3 above to use the non-minified code 55 | $html .= '' . "\n"; 56 | 57 | return array('type'=>'custom', 'html'=>$html); 58 | } 59 | 60 | public function read_post($fieldname) 61 | { 62 | $html = $this->_my_qa_post_text($fieldname); 63 | 64 | return array( 65 | 'format' => 'markdown', 66 | 'content' => $html 67 | ); 68 | } 69 | 70 | public function load_script($fieldname) 71 | { 72 | return 73 | 'var converter = Markdown.getSanitizingConverter();' . "\n" . 74 | 'var editor = new Markdown.Editor(converter, "-'.$fieldname.'");' . "\n" . 75 | 'editor.run();' . "\n"; 76 | } 77 | 78 | 79 | // set admin options 80 | public function admin_form(&$qa_content) 81 | { 82 | $saved_msg = null; 83 | 84 | if (qa_clicked('markdown_save')) { 85 | // save options 86 | $hidecss = qa_post_text('md_hidecss') ? '1' : '0'; 87 | qa_opt($this->cssopt, $hidecss); 88 | $convert = qa_post_text('md_comments') ? '1' : '0'; 89 | qa_opt($this->convopt, $convert); 90 | $convert = qa_post_text('md_highlightjs') ? '1' : '0'; 91 | qa_opt($this->hljsopt, $convert); 92 | $convert = qa_post_text('md_uploadimage') ? '1' : '0'; 93 | qa_opt($this->impuplopt, $convert); 94 | 95 | $saved_msg = qa_lang_html('admin/options_saved'); 96 | } 97 | 98 | 99 | return array( 100 | 'ok' => $saved_msg, 101 | 'style' => 'wide', 102 | 103 | 'fields' => array( 104 | 'css' => array( 105 | 'type' => 'checkbox', 106 | 'label' => qa_lang_html('markdown/admin_hidecss'), 107 | 'tags' => 'NAME="md_hidecss"', 108 | 'value' => qa_opt($this->cssopt) === '1', 109 | 'note' => qa_lang_html('markdown/admin_hidecss_note'), 110 | ), 111 | 'comments' => array( 112 | 'type' => 'checkbox', 113 | 'label' => qa_lang_html('markdown/admin_comments'), 114 | 'tags' => 'NAME="md_comments"', 115 | 'value' => qa_opt($this->convopt) === '1', 116 | 'note' => qa_lang_html('markdown/admin_comments_note'), 117 | ), 118 | 'highlightjs' => array( 119 | 'type' => 'checkbox', 120 | 'label' => qa_lang_html('markdown/admin_syntax'), 121 | 'tags' => 'NAME="md_highlightjs"', 122 | 'value' => qa_opt($this->hljsopt) === '1', 123 | 'note' => qa_lang_html('markdown/admin_syntax_note'), 124 | ), 125 | 'uploadimage' => array( 126 | 'type' => 'checkbox', 127 | 'label' => qa_lang_html('markdown/admin_image'), 128 | 'tags' => 'NAME="md_uploadimage"', 129 | 'value' => qa_opt($this->impuplopt) === '1', 130 | 'note' => qa_lang_html('markdown/admin_image_note'), 131 | ) 132 | ), 133 | 134 | 'buttons' => array( 135 | 'save' => array( 136 | 'tags' => 'NAME="markdown_save"', 137 | 'label' => qa_lang_html('admin/save_options_button'), 138 | 'value' => '1', 139 | ), 140 | ), 141 | ); 142 | } 143 | 144 | 145 | // copy of qa-base.php > qa_post_text, with trim() function removed. 146 | private function _my_qa_post_text($field) 147 | { 148 | return isset($_POST[$field]) ? preg_replace('/\r\n?/', "\n", qa_gpc_to_string($_POST[$field])) : null; 149 | } 150 | } 151 | -------------------------------------------------------------------------------- /qa-markdown-upload.php: -------------------------------------------------------------------------------- 1 | imguplopt) === '1'; 20 | 21 | if (!$uploadimg) { 22 | $message = qa_lang('users/no_permission'); 23 | 24 | header('Content-type: application/json'); 25 | echo json_encode([ 26 | 'error'=>$message 27 | ]); 28 | } else { 29 | require_once QA_INCLUDE_DIR.'app/upload.php'; 30 | 31 | $upload = qa_upload_file_one( 32 | qa_get_max_upload_size(), // do not restrict upload size 33 | true, // force upload to image only 34 | 500, // max width (px) 35 | 500 // max height (px) 36 | ); 37 | 38 | $message = @$upload['error']; 39 | $url = @$upload['bloburl']; 40 | $id = @$upload['blobid']; 41 | 42 | 43 | header('Content-type: application/json'); 44 | echo json_encode([ 45 | 'error'=>$message, 46 | 'url'=>$url, 47 | 'id'=>$id, 48 | ]); 49 | } 50 | } 51 | 52 | return null; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /qa-markdown-viewer.php: -------------------------------------------------------------------------------- 1 | plugindir = $directory; 14 | } 15 | 16 | public function calc_quality($content, $format) 17 | { 18 | return $format == 'markdown' ? 1.0 : 0.8; 19 | } 20 | 21 | public function get_html($content, $format, $options) 22 | { 23 | if (isset($options['blockwordspreg'])) { 24 | require_once QA_INCLUDE_DIR.'util/string.php'; 25 | $content = qa_block_words_replace($content, $options['blockwordspreg']); 26 | } 27 | 28 | // include customized Markdown parser 29 | require_once 'MyMarkdown.php'; 30 | $md = new MyMarkdown(); 31 | $html = $md->parse($content); 32 | 33 | return qa_sanitize_html($html, @$options['linksnewwindow']); 34 | } 35 | 36 | public function get_text($content, $format, $options) 37 | { 38 | $viewer = qa_load_module('viewer', ''); 39 | $text = $viewer->get_text($content, 'html', array()); 40 | 41 | return $text; 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /qa-md-events.php: -------------------------------------------------------------------------------- 1 | directory = $directory; 18 | $this->urltoroot = $urltoroot; 19 | } 20 | 21 | public function process_event($event, $userid, $handle, $cookieid, $params) 22 | { 23 | // check we have the correct event and the option is set 24 | if ($event != 'a_to_c') 25 | return; 26 | if (!qa_opt($this->convopt)) 27 | return; 28 | 29 | qa_post_set_content($params['postid'], null, null, '', null, null, null, qa_get_logged_in_userid()); 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /qa-md-lang-default.php: -------------------------------------------------------------------------------- 1 | 'Markdown', 9 | 'preview' => 'Preview', 10 | 11 | 'admin_hidecss' => 'Don\'t add CSS inline', 12 | 'admin_hidecss_note' => 'Tick if you added the CSS to your own stylesheet (more efficient).', 13 | 'admin_comments' => 'Plaintext comments', 14 | 'admin_comments_note' => 'Sets a post as plaintext when converting answers to comments.', 15 | 'admin_syntax' => 'Use syntax highlighting', 16 | 'admin_syntax_note' => 'Integrates highlight.js for code blocks.', 17 | 'admin_image' => 'Allow image upload', 18 | 'admin_image_note' => 'Allows images to be uploaded directly through the editor.' 19 | ); 20 | -------------------------------------------------------------------------------- /qa-md-layer.php: -------------------------------------------------------------------------------- 1 | template, $tmpl)) 18 | return; 19 | 20 | $hidecss = qa_opt($this->cssopt) === '1'; 21 | $usehljs = qa_opt($this->hljsopt) === '1'; 22 | $wmd_buttons = QA_HTML_THEME_LAYER_URLTOROOT.'pagedown/wmd-buttons.png'; 23 | 24 | $this->output_raw( 25 | "\n\n"); 43 | 44 | // set up HighlightJS 45 | if ($usehljs) { 46 | $js = file_get_contents(QA_HTML_THEME_LAYER_DIRECTORY.'pagedown/highlightjs-run.js'); 47 | 48 | $this->output_raw( 49 | '' . 50 | '' 51 | ); 52 | } 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /qa-plugin.php: -------------------------------------------------------------------------------- 1 |