├── .gitignore
├── assets
├── fonts
│ ├── icomoon.eot
│ ├── icomoon.ttf
│ ├── icomoon.woff
│ ├── icomoon.svg
│ └── icomoon.dev.svg
├── css
│ └── editor.css
└── js
│ └── marked.js
├── composer.json
├── MarkdowneditorAssets.php
├── LICENSE
├── Markdowneditor.php
└── README.md
/.gitignore:
--------------------------------------------------------------------------------
1 | .idea
--------------------------------------------------------------------------------
/assets/fonts/icomoon.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iJackUA/yii2-lepture-markdown-editor-widget/HEAD/assets/fonts/icomoon.eot
--------------------------------------------------------------------------------
/assets/fonts/icomoon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iJackUA/yii2-lepture-markdown-editor-widget/HEAD/assets/fonts/icomoon.ttf
--------------------------------------------------------------------------------
/assets/fonts/icomoon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/iJackUA/yii2-lepture-markdown-editor-widget/HEAD/assets/fonts/icomoon.woff
--------------------------------------------------------------------------------
/composer.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "ijackua/yii2-lepture-markdown-editor-widget",
3 | "description": "Yii2 widget for Lepture Markdown Editor (https://github.com/lepture/editor) - A markdown editor you really want",
4 | "keywords": ["markdown", "editor", "lepture", "yii", "extension", "widget"],
5 | "homepage": "https://github.com/iJackUA/yii2-lepture-markdown-editor-widget",
6 | "type": "yii2-extension",
7 | "license": "MIT",
8 | "authors": [
9 | {
10 | "name": "Evgeniy Kuzminov",
11 | "email": "kyzminov@gmail.com",
12 | "homepage" : "http://stdout.in"
13 | }
14 | ],
15 | "require": {
16 | "yiisoft/yii2": "*"
17 | },
18 | "autoload": {
19 | "psr-4": {
20 | "ijackua\\lepture\\": ""
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/MarkdowneditorAssets.php:
--------------------------------------------------------------------------------
1 | id)) {
46 | $this->id = $this->hasModel() ? Html::getInputId($this->model, $this->attribute) : $this->getId();
47 | }
48 |
49 | if (empty($this->leptureOptions['element'])) {
50 | $this->leptureOptions['element'] = new JsExpression('$("#' . $this->id . '")[0]');
51 | }
52 |
53 | $jsonOptionsMarked = Json::encode($this->markedOptions);
54 | $script = 'marked.setOptions(' . $jsonOptionsMarked . ');';
55 | $this->view->registerJs($script);
56 | }
57 |
58 | public function run()
59 | {
60 | MarkdowneditorAssets::register($this->view);
61 | $this->registerScripts();
62 |
63 | $this->options['id'] = $this->id;
64 | if ($this->hasModel()) {
65 | $textarea = Html::activeTextArea($this->model, $this->attribute, $this->options);
66 | } else {
67 | $textarea = Html::textArea($this->name, $this->value, $this->options);
68 | }
69 | echo '
' . $textarea . '
';
70 | }
71 |
72 | public function registerScripts()
73 | {
74 | $jsonOptions = Json::encode($this->leptureOptions);
75 | $varName = Inflector::classify('editor' . $this->id);
76 |
77 | $script = "var {$varName} = new Editor(" . $jsonOptions . "); {$varName}.render();";
78 | $this->view->registerJs($script);
79 | }
80 | }
81 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | Yii2 Lepture Markdown Editor widget
2 | ---
3 | [![Latest Version on Packagist][ico-version]][link-packagist]
4 | [![Software License][ico-license]](LICENSE.md)
5 | [![Total Downloads][ico-downloads]][link-downloads]
6 |
7 | Yii2 widget for Lepture Markdown Editor (https://github.com/lepture/editor) - A markdown editor you really want
8 |
9 | ## Demo
10 |
11 | on [http://lab.lepture.com/editor/](http://lab.lepture.com/editor/)
12 |
13 | ## Installation via Composer
14 | add to `require` section of your `composer.json`
15 | `"ijackua/yii2-lepture-markdown-editor-widget"`
16 | and run composer update
17 |
18 | ## Usage example
19 |
20 | ### Active widget
21 |
22 | ```php
23 | use ijackua\lepture\Markdowneditor;
24 |
25 | Markdowneditor::widget(
26 | [
27 | 'model' => $model,
28 | 'attribute' => 'full_text',
29 | ])
30 | ```
31 |
32 | ### Simple widget
33 |
34 | ```php
35 | use ijackua\lepture\Markdowneditor;
36 |
37 | Markdowneditor::widget(
38 | [
39 | 'name' => 'editor',
40 | 'value' => '# Hello world'
41 | ])
42 | ```
43 |
44 | ## Editor options
45 |
46 | see on [official site](https://github.com/lepture/editor)
47 |
48 | ```php
49 | use ijackua\lepture\Markdowneditor;
50 |
51 | Markdowneditor::widget(
52 | [
53 | 'model' => $model,
54 | 'attribute' => 'full_text',
55 | 'leptureOptions' => [
56 | 'toolbar' => false
57 | ]
58 | ])
59 | ```
60 |
61 | ## Marked options (markdown parser used by Lepture Editor)
62 | see on [official Marked site](https://github.com/chjj/marked)
63 |
64 | ```php
65 | use ijackua\lepture\Markdowneditor;
66 |
67 | Markdowneditor::widget(
68 | [
69 | 'model' => $model,
70 | 'attribute' => 'full_text',
71 | 'markedOptions' => [
72 | 'tables' => false
73 | ]
74 | ])
75 | ```
76 |
77 | ## Credits
78 |
79 | - [Ievgen Kuzminov][link-author]
80 | - [All Contributors][link-contributors]
81 |
82 | ## License
83 |
84 | The MIT License (MIT). Please see [License File](LICENSE.md) for more information.
85 |
86 | [ico-version]: https://img.shields.io/packagist/v/ijackua/yii2-lepture-markdown-editor-widget.svg?style=flat-square
87 | [ico-license]: https://img.shields.io/badge/license-MIT-brightgreen.svg?style=flat-square
88 | [ico-downloads]: https://img.shields.io/packagist/dt/ijackua/yii2-lepture-markdown-editor-widget.svg?style=flat-square
89 |
90 | [link-packagist]: https://packagist.org/packages/ijackua/yii2-lepture-markdown-editor-widget
91 | [link-downloads]: https://packagist.org/packages/ijackua/yii2-lepture-markdown-editor-widget
92 | [link-author]: https://github.com/iJackUA
93 | [link-contributors]: ../../contributors
94 |
--------------------------------------------------------------------------------
/assets/fonts/icomoon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/assets/fonts/icomoon.dev.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
--------------------------------------------------------------------------------
/assets/css/editor.css:
--------------------------------------------------------------------------------
1 | @font-face {
2 | font-family: 'icomoon';
3 | src:url('../fonts/icomoon.eot');
4 | src:url('../fonts/icomoon.eot?#iefix') format('embedded-opentype'),
5 | url('../fonts/icomoon.woff') format('woff'),
6 | url('../fonts/icomoon.ttf') format('truetype'),
7 | url('../fonts/icomoon.svg#icomoon') format('svg');
8 | font-weight: normal;
9 | font-style: normal;
10 | }
11 |
12 | /* Use the following CSS code if you want to use data attributes for inserting your icons */
13 | .lepture [data-icon]:before {
14 | font-family: 'icomoon' !important;
15 | content: attr(data-icon);
16 | speak: none;
17 | font-weight: normal;
18 | font-variant: normal;
19 | text-transform: none;
20 | line-height: 1;
21 | -webkit-font-smoothing: antialiased;
22 | }
23 |
24 | /* Use the following CSS code if you want to have a class per icon */
25 |
26 | .lepture [class*="icon-"]
27 | { font-family: 'icomoon' !important;
28 | speak: none;
29 | font-style: normal;
30 | font-weight: normal;
31 | font-variant: normal;
32 | text-transform: none;
33 | line-height: 1;
34 | -webkit-font-smoothing: antialiased;
35 | }
36 |
37 | .lepture [class^="icon-"]:before, .lepture [class*="icon-"]:before,.lepture [class*=" icon-"]:before {
38 | font-family: 'icomoon' !important;
39 | }
40 |
41 | .icon-bold:before {
42 | content: "\e000";
43 | }
44 | .icon-italic:before {
45 | content: "\e001";
46 | }
47 | .icon-quote:before {
48 | content: "\e003";
49 | }
50 | .icon-unordered-list:before {
51 | content: "\e004";
52 | }
53 | .icon-ordered-list:before {
54 | content: "\e005";
55 | }
56 | .icon-link:before {
57 | content: "\e006";
58 | }
59 | .icon-image:before {
60 | content: "\e007";
61 | }
62 | .icon-play:before {
63 | content: "\e008";
64 | }
65 | .icon-music:before {
66 | content: "\e009";
67 | }
68 | .icon-contract:before {
69 | content: "\e00a";
70 | }
71 | .icon-fullscreen:before {
72 | content: "\e00b";
73 | }
74 | .icon-question:before {
75 | content: "\e00c";
76 | }
77 | .icon-info:before {
78 | content: "\e00d";
79 | }
80 | .icon-undo:before {
81 | content: "\e00e";
82 | }
83 | .icon-redo:before {
84 | content: "\e00f";
85 | }
86 | .icon-code:before {
87 | content: "\e011";
88 | }
89 | .icon-preview:before {
90 | content: "\e002";
91 | }
92 | /* BASICS */
93 |
94 | .CodeMirror {
95 | height: 300px;
96 | }
97 | .CodeMirror-scroll {
98 | /* Set scrolling behaviour here */
99 | overflow: auto;
100 | }
101 |
102 | /* PADDING */
103 |
104 | .CodeMirror-lines {
105 | padding: 4px 0; /* Vertical padding around content */
106 | }
107 | .CodeMirror pre {
108 | padding: 0 4px; /* Horizontal padding of content */
109 | }
110 |
111 | .CodeMirror-scrollbar-filler {
112 | background-color: white; /* The little square between H and V scrollbars */
113 | }
114 |
115 | /* CURSOR */
116 | .CodeMirror div.CodeMirror-cursor {
117 | border-left: 1px solid black;
118 | z-index: 3;
119 | }
120 | /* Shown when moving in bi-directional text */
121 | .CodeMirror div.CodeMirror-secondarycursor {
122 | border-left: 1px solid silver;
123 | }
124 | .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor {
125 | width: auto;
126 | border: 0;
127 | background: #7e7;
128 | z-index: 1;
129 | }
130 | /* Can style cursor different in overwrite (non-insert) mode */
131 | .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {}
132 |
133 | /* DEFAULT THEME */
134 |
135 | .cm-s-paper .cm-keyword {color: #555;}
136 | .cm-s-paper .cm-atom {color: #7f8c8d;}
137 | .cm-s-paper .cm-number {color: #7f8c8d;}
138 | .cm-s-paper .cm-def {color: #00f;}
139 | .cm-s-paper .cm-variable {color: black;}
140 | .cm-s-paper .cm-variable-2 {color: #555;}
141 | .cm-s-paper .cm-variable-3 {color: #085;}
142 | .cm-s-paper .cm-property {color: black;}
143 | .cm-s-paper .cm-operator {color: black;}
144 | .cm-s-paper .cm-comment {color: #959595;}
145 | .cm-s-paper .cm-string {color: #7f8c8d;}
146 | .cm-s-paper .cm-string-2 {color: #f50;}
147 | .cm-s-paper .cm-meta {color: #555;}
148 | .cm-s-paper .cm-error {color: #f00;}
149 | .cm-s-paper .cm-qualifier {color: #555;}
150 | .cm-s-paper .cm-builtin {color: #555;}
151 | .cm-s-paper .cm-bracket {color: #997;}
152 | .cm-s-paper .cm-tag {color: #7f8c8d;}
153 | .cm-s-paper .cm-attribute {color: #7f8c8d;}
154 | .cm-s-paper .cm-header {color: #000;}
155 | .cm-s-paper .cm-quote {color: #888;}
156 | .cm-s-paper .cm-hr {color: #999;}
157 | .cm-s-paper .cm-link {color: #7f8c8d;}
158 |
159 | .cm-negative {color: #d44;}
160 | .cm-positive {color: #292;}
161 | .cm-header, .cm-strong {font-weight: bold;}
162 | .cm-em {font-style: italic;}
163 | .cm-link {text-decoration: underline;}
164 |
165 | .cm-invalidchar {color: #f00;}
166 |
167 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;}
168 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;}
169 |
170 |
171 | /* STOP */
172 |
173 | /* The rest of this file contains styles related to the mechanics of
174 | the editor. You probably shouldn't touch them. */
175 |
176 | .CodeMirror {
177 | position: relative;
178 | overflow: hidden;
179 | }
180 |
181 | .CodeMirror-scroll {
182 | /* 30px is the magic margin used to hide the element's real scrollbars */
183 | /* See overflow: hidden in .CodeMirror, and the paddings in .CodeMirror-sizer */
184 | margin-bottom: -30px; margin-right: -30px;
185 | padding-bottom: 30px; padding-right: 30px;
186 | height: 100%;
187 | outline: none; /* Prevent dragging from highlighting the element */
188 | position: relative;
189 | }
190 | .CodeMirror-sizer {
191 | position: relative;
192 | }
193 |
194 | /* The fake, visible scrollbars. Used to force redraw during scrolling
195 | before actuall scrolling happens, thus preventing shaking and
196 | flickering artifacts. */
197 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler {
198 | position: absolute;
199 | z-index: 6;
200 | display: none;
201 | }
202 | .CodeMirror-vscrollbar {
203 | right: 0; top: 0;
204 | overflow-x: hidden;
205 | overflow-y: scroll;
206 | }
207 | .CodeMirror-hscrollbar {
208 | bottom: 0; left: 0;
209 | overflow-y: hidden;
210 | overflow-x: scroll;
211 | }
212 | .CodeMirror-scrollbar-filler {
213 | right: 0; bottom: 0;
214 | z-index: 6;
215 | }
216 |
217 | .CodeMirror-lines {
218 | cursor: text;
219 | }
220 | .CodeMirror pre {
221 | /* Reset some styles that the rest of the page might have set */
222 | -moz-border-radius: 0; -webkit-border-radius: 0; -o-border-radius: 0; border-radius: 0;
223 | border-width: 0;
224 | background: transparent;
225 | font-family: inherit;
226 | font-size: inherit;
227 | margin: 0;
228 | white-space: pre-wrap;
229 | word-wrap: normal;
230 | line-height: inherit;
231 | color: inherit;
232 | z-index: 2;
233 | position: relative;
234 | overflow: visible;
235 | }
236 | .CodeMirror-wrap pre {
237 | word-wrap: break-word;
238 | white-space: pre-wrap;
239 | word-break: normal;
240 | }
241 | .CodeMirror-linebackground {
242 | position: absolute;
243 | left: 0; right: 0; top: 0; bottom: 0;
244 | z-index: 0;
245 | }
246 |
247 | .CodeMirror-linewidget {
248 | position: relative;
249 | z-index: 2;
250 | overflow: auto;
251 | }
252 |
253 | .CodeMirror-widget {
254 | display: inline-block;
255 | }
256 |
257 | .CodeMirror-wrap .CodeMirror-scroll {
258 | overflow-x: hidden;
259 | }
260 |
261 | .CodeMirror-measure {
262 | position: absolute;
263 | width: 100%; height: 0px;
264 | overflow: hidden;
265 | visibility: hidden;
266 | }
267 | .CodeMirror-measure pre { position: static; }
268 |
269 | .CodeMirror div.CodeMirror-cursor {
270 | position: absolute;
271 | visibility: hidden;
272 | border-right: none;
273 | width: 0;
274 | }
275 | .CodeMirror-focused div.CodeMirror-cursor {
276 | visibility: visible;
277 | }
278 |
279 | .CodeMirror-selected { background: #d9d9d9; }
280 | .CodeMirror-focused .CodeMirror-selected { background: #BDC3C7; }
281 |
282 | .cm-searching {
283 | background: #ffa;
284 | background: rgba(255, 255, 0, .4);
285 | }
286 |
287 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */
288 | .CodeMirror span { *vertical-align: text-bottom; }
289 |
290 | @media print {
291 | /* Hide the cursor when printing */
292 | .CodeMirror div.CodeMirror-cursor {
293 | visibility: hidden;
294 | }
295 | }
296 | .CodeMirror {
297 | height: 450px;
298 | }
299 | :-webkit-full-screen {
300 | background: #f9f9f5;
301 | padding: 0.5em 1em;
302 | width: 100% !important;
303 | height: 100% !important;
304 | }
305 | :-moz-full-screen {
306 | padding: 0.5em 1em;
307 | background: #f9f9f5;
308 | width: 100% !important;
309 | height: 100% !important;
310 | }
311 | .editor-wrapper {
312 | font: 16px/1.62 "Helvetica Neue", "Xin Gothic", "Hiragino Sans GB", "WenQuanYi Micro Hei", "Microsoft YaHei", sans-serif;
313 | color: #2c3e50;
314 | }
315 | /* this is the title */
316 | .editor-wrapper input.title {
317 | font: 18px "Helvetica Neue", "Xin Gothic", "Hiragino Sans GB", "WenQuanYi Micro Hei", "Microsoft YaHei", sans-serif;
318 | background: transparent;
319 | padding: 4px;
320 | width: 100%;
321 | border: none;
322 | outline: none;
323 | opacity: 0.6;
324 | }
325 | .editor-toolbar {
326 | position: relative;
327 | opacity: 0.6;
328 | }
329 | .editor-toolbar:before, .editor-toolbar:after {
330 | display: block;
331 | content: ' ';
332 | height: 1px;
333 | background-color: #bdc3c7;
334 | background: -moz-linear-gradient(45deg, #f9f9f9, #bdc3c7, #f9f9f9);
335 | background: -webkit-linear-gradient(45deg, #f9f9f9, #bdc3c7, #f9f9f9);
336 | background: -ms-linear-gradient(45deg, #f9f9f9, #bdc3c7, #f9f9f9);
337 | background: linear-gradient(45deg, #f9f9f9, #bdc3c7, #f9f9f9);
338 | }
339 | .editor-toolbar:before {
340 | margin-bottom: 8px;
341 | }
342 | .editor-toolbar:after {
343 | margin-top: 8px;
344 | }
345 | .editor-wrapper input.title:hover, .editor-wrapper input.title:focus, .editor-toolbar:hover {
346 | opacity: 0.8;
347 | }
348 | .editor-toolbar a {
349 | display: inline-block;
350 | text-align: center;
351 | text-decoration: none !important;
352 | color: #2c3e50 !important;
353 | width: 24px;
354 | height: 24px;
355 | margin: 2px;
356 | border: 1px solid transparent;
357 | border-radius: 3px;
358 | cursor: pointer;
359 | }
360 | .editor-toolbar a:hover, .editor-toolbar a.active {
361 | background: #fcfcfc;
362 | border-color: #95a5a6;
363 | }
364 | .editor-toolbar a:before {
365 | line-height: 24px;
366 | }
367 | .editor-toolbar i.separator {
368 | display: inline-block;
369 | width: 0;
370 | border-left: 1px solid #d9d9d9;
371 | border-right: 1px solid white;
372 | color: transparent;
373 | text-indent: -10px;
374 | margin: 0 6px;
375 | }
376 | .editor-toolbar a.icon-fullscreen {
377 | position: absolute;
378 | right: 0;
379 | }
380 | .editor-statusbar {
381 | border-top: 1px solid #ece9e9;
382 | padding: 8px 10px;
383 | font-size: 12px;
384 | color: #959694;
385 | text-align: right;
386 | }
387 | .editor-statusbar span {
388 | display: inline-block;
389 | min-width: 4em;
390 | margin-left: 1em;
391 | }
392 | .editor-statusbar .lines:before {
393 | content: 'lines: ';
394 | }
395 | .editor-statusbar .words:before {
396 | content: 'words: ';
397 | }
398 | .editor-preview {
399 | position: absolute;
400 | width: 100%;
401 | height: 100%;
402 | top: 0;
403 | left: 100%;
404 | background: #f9f9f5;
405 | z-index: 9999;
406 | overflow: auto;
407 | -webkit-transition: left 0.2s ease;
408 | -moz-transition: left 0.2s ease;
409 | -ms-transition: left 0.2s ease;
410 | transition: left 0.2s ease;
411 | }
412 | .editor-preview-active {
413 | left: 0;
414 | }
415 | .editor-preview > p {
416 | margin-top: 0;
417 | }
418 |
--------------------------------------------------------------------------------
/assets/js/marked.js:
--------------------------------------------------------------------------------
1 | /**
2 | * marked - a markdown parser
3 | * Copyright (c) 2011-2013, Christopher Jeffrey. (MIT Licensed)
4 | * https://github.com/chjj/marked
5 | */
6 |
7 | ;(function() {
8 |
9 | /**
10 | * Block-Level Grammar
11 | */
12 |
13 | var block = {
14 | newline: /^\n+/,
15 | code: /^( {4}[^\n]+\n*)+/,
16 | fences: noop,
17 | hr: /^( *[-*_]){3,} *(?:\n+|$)/,
18 | heading: /^ *(#{1,6}) *([^\n]+?) *#* *(?:\n+|$)/,
19 | nptable: noop,
20 | lheading: /^([^\n]+)\n *(=|-){2,} *(?:\n+|$)/,
21 | blockquote: /^( *>[^\n]+(\n[^\n]+)*\n*)+/,
22 | list: /^( *)(bull) [\s\S]+?(?:hr|\n{2,}(?! )(?!\1bull )\n*|\s*$)/,
23 | html: /^ *(?:comment|closed|closing) *(?:\n{2,}|\s*$)/,
24 | def: /^ *\[([^\]]+)\]: *([^\s>]+)>?(?: +["(]([^\n]+)[")])? *(?:\n+|$)/,
25 | table: noop,
26 | paragraph: /^((?:[^\n]+\n?(?!hr|heading|lheading|blockquote|tag|def))+)\n*/,
27 | text: /^[^\n]+/
28 | };
29 |
30 | block.bullet = /(?:[*+-]|\d+\.)/;
31 | block.item = /^( *)(bull) [^\n]*(?:\n(?!\1bull )[^\n]*)*/;
32 | block.item = replace(block.item, 'gm')
33 | (/bull/g, block.bullet)
34 | ();
35 |
36 | block.list = replace(block.list)
37 | (/bull/g, block.bullet)
38 | ('hr', /\n+(?=(?: *[-*_]){3,} *(?:\n+|$))/)
39 | ();
40 |
41 | block._tag = '(?!(?:'
42 | + 'a|em|strong|small|s|cite|q|dfn|abbr|data|time|code'
43 | + '|var|samp|kbd|sub|sup|i|b|u|mark|ruby|rt|rp|bdi|bdo'
44 | + '|span|br|wbr|ins|del|img)\\b)\\w+(?!:/|@)\\b';
45 |
46 | block.html = replace(block.html)
47 | ('comment', //)
48 | ('closed', /<(tag)[\s\S]+?<\/\1>/)
49 | ('closing', /])*?>/)
50 | (/tag/g, block._tag)
51 | ();
52 |
53 | block.paragraph = replace(block.paragraph)
54 | ('hr', block.hr)
55 | ('heading', block.heading)
56 | ('lheading', block.lheading)
57 | ('blockquote', block.blockquote)
58 | ('tag', '<' + block._tag)
59 | ('def', block.def)
60 | ();
61 |
62 | /**
63 | * Normal Block Grammar
64 | */
65 |
66 | block.normal = merge({}, block);
67 |
68 | /**
69 | * GFM Block Grammar
70 | */
71 |
72 | block.gfm = merge({}, block.normal, {
73 | fences: /^ *(`{3,}|~{3,}) *(\S+)? *\n([\s\S]+?)\s*\1 *(?:\n+|$)/,
74 | paragraph: /^/
75 | });
76 |
77 | block.gfm.paragraph = replace(block.paragraph)
78 | ('(?!', '(?!'
79 | + block.gfm.fences.source.replace('\\1', '\\2') + '|'
80 | + block.list.source.replace('\\1', '\\3') + '|')
81 | ();
82 |
83 | /**
84 | * GFM + Tables Block Grammar
85 | */
86 |
87 | block.tables = merge({}, block.gfm, {
88 | nptable: /^ *(\S.*\|.*)\n *([-:]+ *\|[-| :]*)\n((?:.*\|.*(?:\n|$))*)\n*/,
89 | table: /^ *\|(.+)\n *\|( *[-:]+[-| :]*)\n((?: *\|.*(?:\n|$))*)\n*/
90 | });
91 |
92 | /**
93 | * Block Lexer
94 | */
95 |
96 | function Lexer(options) {
97 | this.tokens = [];
98 | this.tokens.links = {};
99 | this.options = options || marked.defaults;
100 | this.rules = block.normal;
101 |
102 | if (this.options.gfm) {
103 | if (this.options.tables) {
104 | this.rules = block.tables;
105 | } else {
106 | this.rules = block.gfm;
107 | }
108 | }
109 | }
110 |
111 | /**
112 | * Expose Block Rules
113 | */
114 |
115 | Lexer.rules = block;
116 |
117 | /**
118 | * Static Lex Method
119 | */
120 |
121 | Lexer.lex = function(src, options) {
122 | var lexer = new Lexer(options);
123 | return lexer.lex(src);
124 | };
125 |
126 | /**
127 | * Preprocessing
128 | */
129 |
130 | Lexer.prototype.lex = function(src) {
131 | src = src
132 | .replace(/\r\n|\r/g, '\n')
133 | .replace(/\t/g, ' ')
134 | .replace(/\u00a0/g, ' ')
135 | .replace(/\u2424/g, '\n');
136 |
137 | return this.token(src, true);
138 | };
139 |
140 | /**
141 | * Lexing
142 | */
143 |
144 | Lexer.prototype.token = function(src, top) {
145 | var src = src.replace(/^ +$/gm, '')
146 | , next
147 | , loose
148 | , cap
149 | , bull
150 | , b
151 | , item
152 | , space
153 | , i
154 | , l;
155 |
156 | while (src) {
157 | // newline
158 | if (cap = this.rules.newline.exec(src)) {
159 | src = src.substring(cap[0].length);
160 | if (cap[0].length > 1) {
161 | this.tokens.push({
162 | type: 'space'
163 | });
164 | }
165 | }
166 |
167 | // code
168 | if (cap = this.rules.code.exec(src)) {
169 | src = src.substring(cap[0].length);
170 | cap = cap[0].replace(/^ {4}/gm, '');
171 | this.tokens.push({
172 | type: 'code',
173 | text: !this.options.pedantic
174 | ? cap.replace(/\n+$/, '')
175 | : cap
176 | });
177 | continue;
178 | }
179 |
180 | // fences (gfm)
181 | if (cap = this.rules.fences.exec(src)) {
182 | src = src.substring(cap[0].length);
183 | this.tokens.push({
184 | type: 'code',
185 | lang: cap[2],
186 | text: cap[3]
187 | });
188 | continue;
189 | }
190 |
191 | // heading
192 | if (cap = this.rules.heading.exec(src)) {
193 | src = src.substring(cap[0].length);
194 | this.tokens.push({
195 | type: 'heading',
196 | depth: cap[1].length,
197 | text: cap[2]
198 | });
199 | continue;
200 | }
201 |
202 | // table no leading pipe (gfm)
203 | if (top && (cap = this.rules.nptable.exec(src))) {
204 | src = src.substring(cap[0].length);
205 |
206 | item = {
207 | type: 'table',
208 | header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
209 | align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
210 | cells: cap[3].replace(/\n$/, '').split('\n')
211 | };
212 |
213 | for (i = 0; i < item.align.length; i++) {
214 | if (/^ *-+: *$/.test(item.align[i])) {
215 | item.align[i] = 'right';
216 | } else if (/^ *:-+: *$/.test(item.align[i])) {
217 | item.align[i] = 'center';
218 | } else if (/^ *:-+ *$/.test(item.align[i])) {
219 | item.align[i] = 'left';
220 | } else {
221 | item.align[i] = null;
222 | }
223 | }
224 |
225 | for (i = 0; i < item.cells.length; i++) {
226 | item.cells[i] = item.cells[i].split(/ *\| */);
227 | }
228 |
229 | this.tokens.push(item);
230 |
231 | continue;
232 | }
233 |
234 | // lheading
235 | if (cap = this.rules.lheading.exec(src)) {
236 | src = src.substring(cap[0].length);
237 | this.tokens.push({
238 | type: 'heading',
239 | depth: cap[2] === '=' ? 1 : 2,
240 | text: cap[1]
241 | });
242 | continue;
243 | }
244 |
245 | // hr
246 | if (cap = this.rules.hr.exec(src)) {
247 | src = src.substring(cap[0].length);
248 | this.tokens.push({
249 | type: 'hr'
250 | });
251 | continue;
252 | }
253 |
254 | // blockquote
255 | if (cap = this.rules.blockquote.exec(src)) {
256 | src = src.substring(cap[0].length);
257 |
258 | this.tokens.push({
259 | type: 'blockquote_start'
260 | });
261 |
262 | cap = cap[0].replace(/^ *> ?/gm, '');
263 |
264 | // Pass `top` to keep the current
265 | // "toplevel" state. This is exactly
266 | // how markdown.pl works.
267 | this.token(cap, top);
268 |
269 | this.tokens.push({
270 | type: 'blockquote_end'
271 | });
272 |
273 | continue;
274 | }
275 |
276 | // list
277 | if (cap = this.rules.list.exec(src)) {
278 | src = src.substring(cap[0].length);
279 | bull = cap[2];
280 |
281 | this.tokens.push({
282 | type: 'list_start',
283 | ordered: bull.length > 1
284 | });
285 |
286 | // Get each top-level item.
287 | cap = cap[0].match(this.rules.item);
288 |
289 | next = false;
290 | l = cap.length;
291 | i = 0;
292 |
293 | for (; i < l; i++) {
294 | item = cap[i];
295 |
296 | // Remove the list item's bullet
297 | // so it is seen as the next token.
298 | space = item.length;
299 | item = item.replace(/^ *([*+-]|\d+\.) +/, '');
300 |
301 | // Outdent whatever the
302 | // list item contains. Hacky.
303 | if (~item.indexOf('\n ')) {
304 | space -= item.length;
305 | item = !this.options.pedantic
306 | ? item.replace(new RegExp('^ {1,' + space + '}', 'gm'), '')
307 | : item.replace(/^ {1,4}/gm, '');
308 | }
309 |
310 | // Determine whether the next list item belongs here.
311 | // Backpedal if it does not belong in this list.
312 | if (this.options.smartLists && i !== l - 1) {
313 | b = block.bullet.exec(cap[i + 1])[0];
314 | if (bull !== b && !(bull.length > 1 && b.length > 1)) {
315 | src = cap.slice(i + 1).join('\n') + src;
316 | i = l - 1;
317 | }
318 | }
319 |
320 | // Determine whether item is loose or not.
321 | // Use: /(^|\n)(?! )[^\n]+\n\n(?!\s*$)/
322 | // for discount behavior.
323 | loose = next || /\n\n(?!\s*$)/.test(item);
324 | if (i !== l - 1) {
325 | next = item.charAt(item.length - 1) === '\n';
326 | if (!loose) loose = next;
327 | }
328 |
329 | this.tokens.push({
330 | type: loose
331 | ? 'loose_item_start'
332 | : 'list_item_start'
333 | });
334 |
335 | // Recurse.
336 | this.token(item, false);
337 |
338 | this.tokens.push({
339 | type: 'list_item_end'
340 | });
341 | }
342 |
343 | this.tokens.push({
344 | type: 'list_end'
345 | });
346 |
347 | continue;
348 | }
349 |
350 | // html
351 | if (cap = this.rules.html.exec(src)) {
352 | src = src.substring(cap[0].length);
353 | this.tokens.push({
354 | type: this.options.sanitize
355 | ? 'paragraph'
356 | : 'html',
357 | pre: cap[1] === 'pre' || cap[1] === 'script' || cap[1] === 'style',
358 | text: cap[0]
359 | });
360 | continue;
361 | }
362 |
363 | // def
364 | if (top && (cap = this.rules.def.exec(src))) {
365 | src = src.substring(cap[0].length);
366 | this.tokens.links[cap[1].toLowerCase()] = {
367 | href: cap[2],
368 | title: cap[3]
369 | };
370 | continue;
371 | }
372 |
373 | // table (gfm)
374 | if (top && (cap = this.rules.table.exec(src))) {
375 | src = src.substring(cap[0].length);
376 |
377 | item = {
378 | type: 'table',
379 | header: cap[1].replace(/^ *| *\| *$/g, '').split(/ *\| */),
380 | align: cap[2].replace(/^ *|\| *$/g, '').split(/ *\| */),
381 | cells: cap[3].replace(/(?: *\| *)?\n$/, '').split('\n')
382 | };
383 |
384 | for (i = 0; i < item.align.length; i++) {
385 | if (/^ *-+: *$/.test(item.align[i])) {
386 | item.align[i] = 'right';
387 | } else if (/^ *:-+: *$/.test(item.align[i])) {
388 | item.align[i] = 'center';
389 | } else if (/^ *:-+ *$/.test(item.align[i])) {
390 | item.align[i] = 'left';
391 | } else {
392 | item.align[i] = null;
393 | }
394 | }
395 |
396 | for (i = 0; i < item.cells.length; i++) {
397 | item.cells[i] = item.cells[i]
398 | .replace(/^ *\| *| *\| *$/g, '')
399 | .split(/ *\| */);
400 | }
401 |
402 | this.tokens.push(item);
403 |
404 | continue;
405 | }
406 |
407 | // top-level paragraph
408 | if (top && (cap = this.rules.paragraph.exec(src))) {
409 | src = src.substring(cap[0].length);
410 | this.tokens.push({
411 | type: 'paragraph',
412 | text: cap[1].charAt(cap[1].length - 1) === '\n'
413 | ? cap[1].slice(0, -1)
414 | : cap[1]
415 | });
416 | continue;
417 | }
418 |
419 | // text
420 | if (cap = this.rules.text.exec(src)) {
421 | // Top-level should never reach here.
422 | src = src.substring(cap[0].length);
423 | this.tokens.push({
424 | type: 'text',
425 | text: cap[0]
426 | });
427 | continue;
428 | }
429 |
430 | if (src) {
431 | throw new
432 | Error('Infinite loop on byte: ' + src.charCodeAt(0));
433 | }
434 | }
435 |
436 | return this.tokens;
437 | };
438 |
439 | /**
440 | * Inline-Level Grammar
441 | */
442 |
443 | var inline = {
444 | escape: /^\\([\\`*{}\[\]()#+\-.!_>])/,
445 | autolink: /^<([^ >]+(@|:\/)[^ >]+)>/,
446 | url: noop,
447 | tag: /^|^<\/?\w+(?:"[^"]*"|'[^']*'|[^'">])*?>/,
448 | link: /^!?\[(inside)\]\(href\)/,
449 | reflink: /^!?\[(inside)\]\s*\[([^\]]*)\]/,
450 | nolink: /^!?\[((?:\[[^\]]*\]|[^\[\]])*)\]/,
451 | strong: /^__([\s\S]+?)__(?!_)|^\*\*([\s\S]+?)\*\*(?!\*)/,
452 | em: /^\b_((?:__|[\s\S])+?)_\b|^\*((?:\*\*|[\s\S])+?)\*(?!\*)/,
453 | code: /^(`+)\s*([\s\S]*?[^`])\s*\1(?!`)/,
454 | br: /^ {2,}\n(?!\s*$)/,
455 | del: noop,
456 | text: /^[\s\S]+?(?=[\\?(?:\s+['"]([\s\S]*?)['"])?\s*/;
461 |
462 | inline.link = replace(inline.link)
463 | ('inside', inline._inside)
464 | ('href', inline._href)
465 | ();
466 |
467 | inline.reflink = replace(inline.reflink)
468 | ('inside', inline._inside)
469 | ();
470 |
471 | /**
472 | * Normal Inline Grammar
473 | */
474 |
475 | inline.normal = merge({}, inline);
476 |
477 | /**
478 | * Pedantic Inline Grammar
479 | */
480 |
481 | inline.pedantic = merge({}, inline.normal, {
482 | strong: /^__(?=\S)([\s\S]*?\S)__(?!_)|^\*\*(?=\S)([\s\S]*?\S)\*\*(?!\*)/,
483 | em: /^_(?=\S)([\s\S]*?\S)_(?!_)|^\*(?=\S)([\s\S]*?\S)\*(?!\*)/
484 | });
485 |
486 | /**
487 | * GFM Inline Grammar
488 | */
489 |
490 | inline.gfm = merge({}, inline.normal, {
491 | escape: replace(inline.escape)('])', '~|])')(),
492 | url: /^(https?:\/\/[^\s<]+[^<.,:;"')\]\s])/,
493 | del: /^~~(?=\S)([\s\S]*?\S)~~/,
494 | text: replace(inline.text)
495 | (']|', '~]|')
496 | ('|', '|https?://|')
497 | ()
498 | });
499 |
500 | /**
501 | * GFM + Line Breaks Inline Grammar
502 | */
503 |
504 | inline.breaks = merge({}, inline.gfm, {
505 | br: replace(inline.br)('{2,}', '*')(),
506 | text: replace(inline.gfm.text)('{2,}', '*')()
507 | });
508 |
509 | /**
510 | * Inline Lexer & Compiler
511 | */
512 |
513 | function InlineLexer(links, options) {
514 | this.options = options || marked.defaults;
515 | this.links = links;
516 | this.rules = inline.normal;
517 |
518 | if (!this.links) {
519 | throw new
520 | Error('Tokens array requires a `links` property.');
521 | }
522 |
523 | if (this.options.gfm) {
524 | if (this.options.breaks) {
525 | this.rules = inline.breaks;
526 | } else {
527 | this.rules = inline.gfm;
528 | }
529 | } else if (this.options.pedantic) {
530 | this.rules = inline.pedantic;
531 | }
532 | }
533 |
534 | /**
535 | * Expose Inline Rules
536 | */
537 |
538 | InlineLexer.rules = inline;
539 |
540 | /**
541 | * Static Lexing/Compiling Method
542 | */
543 |
544 | InlineLexer.output = function(src, links, options) {
545 | var inline = new InlineLexer(links, options);
546 | return inline.output(src);
547 | };
548 |
549 | /**
550 | * Lexing/Compiling
551 | */
552 |
553 | InlineLexer.prototype.output = function(src) {
554 | var out = ''
555 | , link
556 | , text
557 | , href
558 | , cap;
559 |
560 | while (src) {
561 | // escape
562 | if (cap = this.rules.escape.exec(src)) {
563 | src = src.substring(cap[0].length);
564 | out += cap[1];
565 | continue;
566 | }
567 |
568 | // autolink
569 | if (cap = this.rules.autolink.exec(src)) {
570 | src = src.substring(cap[0].length);
571 | if (cap[2] === '@') {
572 | text = cap[1].charAt(6) === ':'
573 | ? this.mangle(cap[1].substring(7))
574 | : this.mangle(cap[1]);
575 | href = this.mangle('mailto:') + text;
576 | } else {
577 | text = escape(cap[1]);
578 | href = text;
579 | }
580 | out += ''
583 | + text
584 | + '';
585 | continue;
586 | }
587 |
588 | // url (gfm)
589 | if (cap = this.rules.url.exec(src)) {
590 | src = src.substring(cap[0].length);
591 | text = escape(cap[1]);
592 | href = text;
593 | out += ''
596 | + text
597 | + '';
598 | continue;
599 | }
600 |
601 | // tag
602 | if (cap = this.rules.tag.exec(src)) {
603 | src = src.substring(cap[0].length);
604 | out += this.options.sanitize
605 | ? escape(cap[0])
606 | : cap[0];
607 | continue;
608 | }
609 |
610 | // link
611 | if (cap = this.rules.link.exec(src)) {
612 | src = src.substring(cap[0].length);
613 | out += this.outputLink(cap, {
614 | href: cap[2],
615 | title: cap[3]
616 | });
617 | continue;
618 | }
619 |
620 | // reflink, nolink
621 | if ((cap = this.rules.reflink.exec(src))
622 | || (cap = this.rules.nolink.exec(src))) {
623 | src = src.substring(cap[0].length);
624 | link = (cap[2] || cap[1]).replace(/\s+/g, ' ');
625 | link = this.links[link.toLowerCase()];
626 | if (!link || !link.href) {
627 | out += cap[0].charAt(0);
628 | src = cap[0].substring(1) + src;
629 | continue;
630 | }
631 | out += this.outputLink(cap, link);
632 | continue;
633 | }
634 |
635 | // strong
636 | if (cap = this.rules.strong.exec(src)) {
637 | src = src.substring(cap[0].length);
638 | out += ''
639 | + this.output(cap[2] || cap[1])
640 | + '';
641 | continue;
642 | }
643 |
644 | // em
645 | if (cap = this.rules.em.exec(src)) {
646 | src = src.substring(cap[0].length);
647 | out += ''
648 | + this.output(cap[2] || cap[1])
649 | + '';
650 | continue;
651 | }
652 |
653 | // code
654 | if (cap = this.rules.code.exec(src)) {
655 | src = src.substring(cap[0].length);
656 | out += ''
657 | + escape(cap[2], true)
658 | + '';
659 | continue;
660 | }
661 |
662 | // br
663 | if (cap = this.rules.br.exec(src)) {
664 | src = src.substring(cap[0].length);
665 | out += '
';
666 | continue;
667 | }
668 |
669 | // del (gfm)
670 | if (cap = this.rules.del.exec(src)) {
671 | src = src.substring(cap[0].length);
672 | out += ''
673 | + this.output(cap[1])
674 | + '';
675 | continue;
676 | }
677 |
678 | // text
679 | if (cap = this.rules.text.exec(src)) {
680 | src = src.substring(cap[0].length);
681 | out += escape(this.smartypants(cap[0]));
682 | continue;
683 | }
684 |
685 | if (src) {
686 | throw new
687 | Error('Infinite loop on byte: ' + src.charCodeAt(0));
688 | }
689 | }
690 |
691 | return out;
692 | };
693 |
694 | /**
695 | * Compile Link
696 | */
697 |
698 | InlineLexer.prototype.outputLink = function(cap, link) {
699 | if (cap[0].charAt(0) !== '!') {
700 | return ''
709 | + this.output(cap[1])
710 | + '';
711 | } else {
712 | return '
';
723 | }
724 | };
725 |
726 | /**
727 | * Smartypants Transformations
728 | */
729 |
730 | InlineLexer.prototype.smartypants = function(text) {
731 | if (!this.options.smartypants) return text;
732 | return text
733 | // em-dashes
734 | .replace(/--/g, '\u2014')
735 | // opening singles
736 | .replace(/(^|[-\u2014/(\[{"\s])'/g, '$1\u2018')
737 | // closing singles & apostrophes
738 | .replace(/'/g, '\u2019')
739 | // opening doubles
740 | .replace(/(^|[-\u2014/(\[{\u2018\s])"/g, '$1\u201c')
741 | // closing doubles
742 | .replace(/"/g, '\u201d')
743 | // ellipses
744 | .replace(/\.{3}/g, '\u2026');
745 | };
746 |
747 | /**
748 | * Mangle Links
749 | */
750 |
751 | InlineLexer.prototype.mangle = function(text) {
752 | var out = ''
753 | , l = text.length
754 | , i = 0
755 | , ch;
756 |
757 | for (; i < l; i++) {
758 | ch = text.charCodeAt(i);
759 | if (Math.random() > 0.5) {
760 | ch = 'x' + ch.toString(16);
761 | }
762 | out += '' + ch + ';';
763 | }
764 |
765 | return out;
766 | };
767 |
768 | /**
769 | * Parsing & Compiling
770 | */
771 |
772 | function Parser(options) {
773 | this.tokens = [];
774 | this.token = null;
775 | this.options = options || marked.defaults;
776 | }
777 |
778 | /**
779 | * Static Parse Method
780 | */
781 |
782 | Parser.parse = function(src, options) {
783 | var parser = new Parser(options);
784 | return parser.parse(src);
785 | };
786 |
787 | /**
788 | * Parse Loop
789 | */
790 |
791 | Parser.prototype.parse = function(src) {
792 | this.inline = new InlineLexer(src.links, this.options);
793 | this.tokens = src.reverse();
794 |
795 | var out = '';
796 | while (this.next()) {
797 | out += this.tok();
798 | }
799 |
800 | return out;
801 | };
802 |
803 | /**
804 | * Next Token
805 | */
806 |
807 | Parser.prototype.next = function() {
808 | return this.token = this.tokens.pop();
809 | };
810 |
811 | /**
812 | * Preview Next Token
813 | */
814 |
815 | Parser.prototype.peek = function() {
816 | return this.tokens[this.tokens.length - 1] || 0;
817 | };
818 |
819 | /**
820 | * Parse Text Tokens
821 | */
822 |
823 | Parser.prototype.parseText = function() {
824 | var body = this.token.text;
825 |
826 | while (this.peek().type === 'text') {
827 | body += '\n' + this.next().text;
828 | }
829 |
830 | return this.inline.output(body);
831 | };
832 |
833 | /**
834 | * Parse Current Token
835 | */
836 |
837 | Parser.prototype.tok = function() {
838 | switch (this.token.type) {
839 | case 'space': {
840 | return '';
841 | }
842 | case 'hr': {
843 | return '
\n';
844 | }
845 | case 'heading': {
846 | return ''
851 | + this.inline.output(this.token.text)
852 | + '\n';
855 | }
856 | case 'code': {
857 | if (this.options.highlight) {
858 | var code = this.options.highlight(this.token.text, this.token.lang);
859 | if (code != null && code !== this.token.text) {
860 | this.token.escaped = true;
861 | this.token.text = code;
862 | }
863 | }
864 |
865 | if (!this.token.escaped) {
866 | this.token.text = escape(this.token.text, true);
867 | }
868 |
869 | return ''
877 | + this.token.text
878 | + '
\n';
879 | }
880 | case 'table': {
881 | var body = ''
882 | , heading
883 | , i
884 | , row
885 | , cell
886 | , j;
887 |
888 | // header
889 | body += '\n\n';
890 | for (i = 0; i < this.token.header.length; i++) {
891 | heading = this.inline.output(this.token.header[i]);
892 | body += '| ' + heading + ' | \n';
897 | }
898 | body += '
\n\n';
899 |
900 | // body
901 | body += '\n'
902 | for (i = 0; i < this.token.cells.length; i++) {
903 | row = this.token.cells[i];
904 | body += '\n';
905 | for (j = 0; j < row.length; j++) {
906 | cell = this.inline.output(row[j]);
907 | body += '| ' + cell + ' | \n';
912 | }
913 | body += '
\n';
914 | }
915 | body += '\n';
916 |
917 | return '\n'
918 | + body
919 | + '
\n';
920 | }
921 | case 'blockquote_start': {
922 | var body = '';
923 |
924 | while (this.next().type !== 'blockquote_end') {
925 | body += this.tok();
926 | }
927 |
928 | return '\n'
929 | + body
930 | + '
\n';
931 | }
932 | case 'list_start': {
933 | var type = this.token.ordered ? 'ol' : 'ul'
934 | , body = '';
935 |
936 | while (this.next().type !== 'list_end') {
937 | body += this.tok();
938 | }
939 |
940 | return '<'
941 | + type
942 | + '>\n'
943 | + body
944 | + ''
945 | + type
946 | + '>\n';
947 | }
948 | case 'list_item_start': {
949 | var body = '';
950 |
951 | while (this.next().type !== 'list_item_end') {
952 | body += this.token.type === 'text'
953 | ? this.parseText()
954 | : this.tok();
955 | }
956 |
957 | return ''
958 | + body
959 | + '\n';
960 | }
961 | case 'loose_item_start': {
962 | var body = '';
963 |
964 | while (this.next().type !== 'list_item_end') {
965 | body += this.tok();
966 | }
967 |
968 | return ''
969 | + body
970 | + '\n';
971 | }
972 | case 'html': {
973 | return !this.token.pre && !this.options.pedantic
974 | ? this.inline.output(this.token.text)
975 | : this.token.text;
976 | }
977 | case 'paragraph': {
978 | return ''
979 | + this.inline.output(this.token.text)
980 | + '
\n';
981 | }
982 | case 'text': {
983 | return ''
984 | + this.parseText()
985 | + '
\n';
986 | }
987 | }
988 | };
989 |
990 | /**
991 | * Helpers
992 | */
993 |
994 | function escape(html, encode) {
995 | return html
996 | .replace(!encode ? /&(?!#?\w+;)/g : /&/g, '&')
997 | .replace(//g, '>')
999 | .replace(/"/g, '"')
1000 | .replace(/'/g, ''');
1001 | }
1002 |
1003 | function replace(regex, opt) {
1004 | regex = regex.source;
1005 | opt = opt || '';
1006 | return function self(name, val) {
1007 | if (!name) return new RegExp(regex, opt);
1008 | val = val.source || val;
1009 | val = val.replace(/(^|[^\[])\^/g, '$1');
1010 | regex = regex.replace(name, val);
1011 | return self;
1012 | };
1013 | }
1014 |
1015 | function noop() {}
1016 | noop.exec = noop;
1017 |
1018 | function merge(obj) {
1019 | var i = 1
1020 | , target
1021 | , key;
1022 |
1023 | for (; i < arguments.length; i++) {
1024 | target = arguments[i];
1025 | for (key in target) {
1026 | if (Object.prototype.hasOwnProperty.call(target, key)) {
1027 | obj[key] = target[key];
1028 | }
1029 | }
1030 | }
1031 |
1032 | return obj;
1033 | }
1034 |
1035 | /**
1036 | * Marked
1037 | */
1038 |
1039 | function marked(src, opt, callback) {
1040 | if (callback || typeof opt === 'function') {
1041 | if (!callback) {
1042 | callback = opt;
1043 | opt = null;
1044 | }
1045 |
1046 | opt = merge({}, marked.defaults, opt || {});
1047 |
1048 | var highlight = opt.highlight
1049 | , tokens
1050 | , pending
1051 | , i = 0;
1052 |
1053 | try {
1054 | tokens = Lexer.lex(src, opt)
1055 | } catch (e) {
1056 | return callback(e);
1057 | }
1058 |
1059 | pending = tokens.length;
1060 |
1061 | var done = function() {
1062 | var out, err;
1063 |
1064 | try {
1065 | out = Parser.parse(tokens, opt);
1066 | } catch (e) {
1067 | err = e;
1068 | }
1069 |
1070 | opt.highlight = highlight;
1071 |
1072 | return err
1073 | ? callback(err)
1074 | : callback(null, out);
1075 | };
1076 |
1077 | if (!highlight || highlight.length < 3) {
1078 | return done();
1079 | }
1080 |
1081 | delete opt.highlight;
1082 |
1083 | if (!pending) return done();
1084 |
1085 | for (; i < tokens.length; i++) {
1086 | (function(token) {
1087 | if (token.type !== 'code') {
1088 | return --pending || done();
1089 | }
1090 | return highlight(token.text, token.lang, function(err, code) {
1091 | if (code == null || code === token.text) {
1092 | return --pending || done();
1093 | }
1094 | token.text = code;
1095 | token.escaped = true;
1096 | --pending || done();
1097 | });
1098 | })(tokens[i]);
1099 | }
1100 |
1101 | return;
1102 | }
1103 | try {
1104 | if (opt) opt = merge({}, marked.defaults, opt);
1105 | return Parser.parse(Lexer.lex(src, opt), opt);
1106 | } catch (e) {
1107 | e.message += '\nPlease report this to https://github.com/chjj/marked.';
1108 | if ((opt || marked.defaults).silent) {
1109 | return 'An error occured:
'
1110 | + escape(e.message + '', true)
1111 | + '
';
1112 | }
1113 | throw e;
1114 | }
1115 | }
1116 |
1117 | /**
1118 | * Options
1119 | */
1120 |
1121 | marked.options =
1122 | marked.setOptions = function(opt) {
1123 | merge(marked.defaults, opt);
1124 | return marked;
1125 | };
1126 |
1127 | marked.defaults = {
1128 | gfm: true,
1129 | tables: true,
1130 | breaks: false,
1131 | pedantic: false,
1132 | sanitize: false,
1133 | smartLists: false,
1134 | silent: false,
1135 | highlight: null,
1136 | langPrefix: 'lang-',
1137 | smartypants: false
1138 | };
1139 |
1140 | /**
1141 | * Expose
1142 | */
1143 |
1144 | marked.Parser = Parser;
1145 | marked.parser = Parser.parse;
1146 |
1147 | marked.Lexer = Lexer;
1148 | marked.lexer = Lexer.lex;
1149 |
1150 | marked.InlineLexer = InlineLexer;
1151 | marked.inlineLexer = InlineLexer.output;
1152 |
1153 | marked.parse = marked;
1154 |
1155 | if (typeof exports === 'object') {
1156 | module.exports = marked;
1157 | } else if (typeof define === 'function' && define.amd) {
1158 | define(function() { return marked; });
1159 | } else {
1160 | this.marked = marked;
1161 | }
1162 |
1163 | }).call(function() {
1164 | return this || (typeof window !== 'undefined' ? window : global);
1165 | }());
1166 |
--------------------------------------------------------------------------------