├── babel.js
├── .babelrc
├── examples
├── index.html
├── webpack.js
├── index.js
├── prism.css
└── prism.js
├── .gitignore
├── src
├── index.js
├── renderer.js
├── babel.js
└── jsx.js
├── README.md
├── package.json
└── yarn.lock
/babel.js:
--------------------------------------------------------------------------------
1 | module.exports = require('./lib/babel')
2 |
--------------------------------------------------------------------------------
/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": [ "es2015", "stage-0", "react" ],
3 | plugins: [ "transform-decorators-legacy" ]
4 | }
--------------------------------------------------------------------------------
/examples/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # osx noise
2 | .DS_Store
3 | profile
4 |
5 | # xcode noise
6 | build/*
7 | *.mode1
8 | *.mode1v3
9 | *.mode2v3
10 | *.perspective
11 | *.perspectivev3
12 | *.pbxuser
13 | *.xcworkspace
14 | xcuserdata
15 |
16 | # svn & cvs
17 | .svn
18 | CVS
19 | node_modules
20 | lib
21 |
--------------------------------------------------------------------------------
/src/index.js:
--------------------------------------------------------------------------------
1 | const markdown = (o, fn) => {
2 | if((o.length >= 0 && o.raw) || (fn && fn.length && fn.raw)) {
3 | throw new Error('you forgot to add \'markdown-in-js/babel\' to your babel plugins')
4 | }
5 | if(typeof o === 'function') {
6 | return markdown({}, o)
7 | }
8 | if(!fn) {
9 | return nextFn => markdown(o, nextFn)
10 | }
11 | if(typeof fn === 'function') {
12 | return fn({
13 | 'br': 'br', 'a': 'a', 'img': 'img', 'em': 'em', 'strong': 'strong', 'p': 'p',
14 | 'h1': 'h1', 'h2': 'h2', 'h3': 'h3', 'h4': 'h4', 'h5': 'h5', 'h6': 'h6',
15 | 'code': 'code', 'pre': 'pre', 'hr': 'hr', 'blockquote': 'blockquote',
16 | 'ul': 'ul', 'ol': 'ol', 'li': 'li', ...o
17 | })
18 | }
19 | }
20 |
21 | module.exports = markdown
22 |
--------------------------------------------------------------------------------
/examples/webpack.js:
--------------------------------------------------------------------------------
1 | let webpack = require('webpack')
2 | let path = require('path')
3 | module.exports = {
4 | devtool: 'source-map',
5 | entry: './examples/index.js',
6 | output: {
7 | path: path.resolve('./examples'),
8 | filename: 'bundle.js'
9 | },
10 | module: {
11 | rules: [ {
12 | test: /\.js$/,
13 | exclude: /node_modules/,
14 | loader: 'babel-loader',
15 | query: {
16 | plugins: [ path.join(__dirname, '../lib/babel.js') ]
17 | }
18 | }, {
19 | test: /\.json$/,
20 | loader: 'json-loader'
21 | } ]
22 | },
23 | resolve: {
24 | alias: {
25 | 'markdown-in-js': '../src'
26 | }
27 | },
28 | plugins: [
29 | new webpack.DefinePlugin({
30 | 'process.env.NODE_ENV': JSON.stringify(process.env.NODE_ENV || 'development')
31 | })
32 | ],
33 | devServer: {
34 | contentBase: 'examples/',
35 | historyApiFallback: true,
36 | compress: true,
37 | inline: true
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/examples/index.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import { render } from 'react-dom'
3 |
4 | import md from 'markdown-in-js'
5 |
6 | function log() {
7 | console.log(this) //eslint-disable-line
8 | return this
9 | }
10 |
11 | const App = () => md({ h1: 'h2' })`
12 | # title
13 |
14 | This is some text we _here_
15 |
16 | This is more text. And some more. And more.
17 | \`\`\`jsx
18 | let x = alpha
19 | function xyz(){
20 |
21 | }
22 | \`\`\`
23 |
24 | here's some \`inline code\`
25 |
26 | ${'I interpolated some text here'}
27 |
28 | and
29 | this is some inline _italicized_ *bold* html
30 | and this bit is _${'interpolated'}_
31 |
32 |
33 | and what about _this_
34 |
35 | This is an H1
36 | =============
37 |
38 | This is an H2
39 | -------------
40 |
41 | # This is an H1
42 |
43 | ## This is an H2
44 |
45 | ###### This is an H6
46 | `::log()
47 |
48 |
49 | render(, window.app)
50 |
51 |
--------------------------------------------------------------------------------
/src/renderer.js:
--------------------------------------------------------------------------------
1 | export default class Renderer {
2 | render(ast) {
3 | let walker = ast.walker()
4 | , event
5 | , type
6 |
7 | this.buffer = ''
8 | this.lastOut = '\n'
9 |
10 | while((event = walker.next())) {
11 | type = event.node.type
12 | if (this[type]) {
13 | this[type](event.node, event.entering)
14 | }
15 | else {
16 | // console.log('missed', event.node.type)
17 | }
18 | }
19 | return this.buffer
20 | }
21 |
22 | /**
23 | * Concatenate a literal string to the buffer.
24 | *
25 | * @param str {String} The string to concatenate.
26 | */
27 | lit(str) {
28 | this.buffer += str
29 | this.lastOut = str
30 | }
31 |
32 | cr() {
33 | if (this.lastOut !== '\n') {
34 | this.lit('\n')
35 | }
36 | }
37 |
38 | /**
39 | * Concatenate a string to the buffer possibly escaping the content.
40 | *
41 | * Concrete renderer implementations should override this method.
42 | *
43 | * @param str {String} The string to concatenate.
44 | */
45 | out(str) {
46 | this.lit(str)
47 | }
48 |
49 | }
50 |
51 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | markdown-in-js
2 | ---
3 |
4 | zero-overhead markdown in your react components
5 |
6 | usage
7 | ---
8 |
9 | add `'markdown-in-js/babel'` to the `plugins` field of your babel config
10 |
11 | ```jsx
12 | import markdown from 'markdown-in-js'
13 |
14 | const App = () => markdown`
15 | ## This is some content.
16 | You can write _markdown_ as you'd like.
17 |
18 | ${ interpolate more }
19 |
20 | you can inline *html* or even , wow
21 |
22 |
25 | interpolate attributes as expected
26 |
27 | `
28 | ```
29 |
30 | - gets compiled to react elements via a babel plugin
31 | - preserves interpolations
32 | - built with [commonmark](https://github.com/jgm/commonmark.js)
33 | - optionally add [prismjs](http://prismjs.com/) for syntax highlighting of code blocks
34 |
35 | custom components
36 | ---
37 |
38 | You can use custom components for markdown primitives like so -
39 | ```jsx
40 | import md from 'markdown-in-js'
41 | import { MyHeading, MyLink } from './path/to/components'
42 |
43 | const App = () => md({ h1: MyHeading, a: MyLink })`
44 | # this will be a custom header
45 | [custom link component](/url/to/link)
46 | `
47 | ```
48 |
49 | todo
50 | ---
51 |
52 | - optionally no-wrap paragraphs
53 | - optionally return array of elements
54 | - instructions for in-editor syntax highlighting
55 | - add gotchas to docs
56 | - precompile code blocks
57 | - commonmark options
58 | - tests!
59 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "markdown-in-js",
3 | "version": "1.1.4",
4 | "description": "markdown in js ",
5 | "main": "lib/index.js",
6 | "author": "Sunil Pai ",
7 | "license": "MIT",
8 | "files": [
9 | "lib",
10 | "babel.js"
11 | ],
12 | "scripts": {
13 | "start": "webpack-dev-server --config examples/webpack.js",
14 | "build": "babel src -d lib",
15 | "prepublish": "npm run build"
16 | },
17 | "dependencies": {
18 | "babylon": "^6.14.1",
19 | "commonmark": "^0.27.0"
20 | },
21 | "devDependencies": {
22 | "babel-cli": "^6.18.0",
23 | "babel-core": "^6.18.2",
24 | "babel-eslint": "^7.1.1",
25 | "babel-loader": "^6.2.8",
26 | "babel-plugin-istanbul": "^3.0.0",
27 | "babel-plugin-transform-decorators-legacy": "^1.3.4",
28 | "babel-polyfill": "^6.16.0",
29 | "babel-preset-es2015": "^6.18.0",
30 | "babel-preset-react": "^6.16.0",
31 | "babel-preset-stage-0": "^6.16.0",
32 | "eslint": "^3.10.2",
33 | "eslint-config-rackt": "^1.1.1",
34 | "eslint-plugin-react": "^6.7.1",
35 | "json-loader": "^0.5.4",
36 | "react": "^15.4.0",
37 | "react-dom": "^15.4.0",
38 | "webpack": "2.1.0-beta.27",
39 | "webpack-dev-server": "2.1.0-beta.11"
40 | },
41 | "eslintConfig": {
42 | "extends": [
43 | "rackt"
44 | ],
45 | "plugins": [
46 | "react"
47 | ],
48 | "rules": {
49 | "react/jsx-uses-vars": "error",
50 | "react/jsx-uses-react": "error",
51 | "jsx-quotes": 0
52 | }
53 | }
54 | }
55 |
--------------------------------------------------------------------------------
/examples/prism.css:
--------------------------------------------------------------------------------
1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+jsx */
2 | code[class*="language-"],pre[class*="language-"]{color:#000;background:none;text-shadow:0 1px white;font-family:Consolas,Monaco,'Andale Mono','Ubuntu Mono',monospace;text-align:left;white-space:pre;word-spacing:normal;word-break:normal;word-wrap:normal;line-height:1.5;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-hyphens:none;-moz-hyphens:none;-ms-hyphens:none;hyphens:none}pre[class*="language-"]::-moz-selection,pre[class*="language-"] ::-moz-selection,code[class*="language-"]::-moz-selection,code[class*="language-"] ::-moz-selection{text-shadow:none;background:#b3d4fc}pre[class*="language-"]::selection,pre[class*="language-"] ::selection,code[class*="language-"]::selection,code[class*="language-"] ::selection{text-shadow:none;background:#b3d4fc}@media print{code[class*="language-"],pre[class*="language-"]{text-shadow:none}}pre[class*="language-"]{padding:1em;margin:.5em 0;overflow:auto}:not(pre) > code[class*="language-"],
3 | pre[class*="language-"] {background:#f5f2f0}:not(pre) > code[class*="language-"] {padding:.1em;border-radius:.3em;white-space:normal}.token.comment,.token.prolog,.token.doctype,.token.cdata{color:#708090}.token.punctuation{color:#999}.namespace{opacity:.7}.token.property,.token.tag,.token.boolean,.token.number,.token.constant,.token.symbol,.token.deleted{color:#905}.token.selector,.token.attr-name,.token.string,.token.char,.token.builtin,.token.inserted{color:#690}.token.operator,.token.entity,.token.url,.language-css .token.string,.style .token.string{color:#a67f59;background:hsla(0,0%,100%,.5)}.token.atrule,.token.attr-value,.token.keyword{color:#07a}.token.function{color:#dd4a68}.token.regex,.token.important,.token.variable{color:#e90}.token.important,.token.bold{font-weight:bold}.token.italic{font-style:italic}.token.entity{cursor:help}
--------------------------------------------------------------------------------
/src/babel.js:
--------------------------------------------------------------------------------
1 | import { name as packageName } from '../package.json'
2 |
3 | import * as babylon from 'babylon'
4 |
5 | import commonmark from 'commonmark'
6 | import JSXRenderer from './jsx'
7 |
8 |
9 | module.exports = () => {
10 | return {
11 | visitor: {
12 | Program: {
13 | enter(path) {
14 | let libName
15 | path.traverse({
16 | ImportDeclaration(path) {
17 | if (path.node.source.value === packageName) {
18 | const specifiers = path.get('specifiers')
19 | for (const specifier of specifiers) {
20 | if (specifier.isImportDefaultSpecifier()) {
21 | libName = specifier.node.local.name
22 | break
23 | }
24 | else if (specifier.isImportSpecifier() && specifier.node.imported.name === 'default') {
25 | libName = specifier.node.local.name
26 | break
27 | }
28 | }
29 | }
30 | },
31 | CallExpression(path) {
32 | if (!path.get('callee').isIdentifier() || path.node.callee.name !== 'require') {
33 | return
34 | }
35 | const args = path.get('arguments')
36 | const arg = args[0]
37 | if (!arg || !arg.isStringLiteral() || arg.node.value !== packageName) {
38 | return
39 | }
40 | const parent = path.parentPath
41 | if (parent.isVariableDeclarator()) {
42 | const id = parent.get('id')
43 | if (id.isIdentifier()) {
44 | libName = id.name
45 | }
46 | }
47 | else if (parent.isAssignmentExpression()) {
48 | const id = parent.get('left')
49 | if (id.isIdentifier()) {
50 | libName = id.name
51 | }
52 | }
53 | }
54 | })
55 |
56 |
57 | if (!libName) {
58 | // the module is not required in this file.
59 | return
60 | }
61 |
62 | path.traverse({
63 | TaggedTemplateExpression(path) {
64 |
65 | let code = path.hub.file.code
66 | let tagName = path.node.tag.name
67 | if(path.node.tag.type === 'CallExpression') {
68 | tagName = path.node.tag.callee.name
69 | }
70 |
71 | if(tagName === libName) {
72 | let reader = new commonmark.Parser()
73 | let writer = new JSXRenderer()
74 | let stubs = path.node.quasi.expressions.map(x => code.substring(x.start, x.end))
75 | let stubCtx = stubs.reduce((o, stub, i) => (o['spur-' + i] = stub, o), {})
76 | let ctr = 0
77 | let strs = path.node.quasi.quasis.map(x => x.value.cooked)
78 | let src = strs.reduce((arr, str, i) => {
79 | arr.push(str)
80 | if(i !== stubs.length) {
81 | arr.push('spur-'+ctr++)
82 | }
83 | return arr
84 | }, []).join('')
85 | let parsed = reader.parse(src)
86 | let intermediateSrc = writer.render(parsed)
87 | // replace with stubs
88 | let newSrc = intermediateSrc.replace(/spur\-[0-9]+/gm, x => `{${stubCtx[x]}}`)
89 | let transformed = babylon.parse(`${tagName}(${
90 | path.node.tag.type === 'CallExpression' ?
91 | code.substring(path.node.tag.arguments[0].start, path.node.tag.arguments[0].end) + ', ' :
92 | ''
93 | }_m_ => ${newSrc}
)`, { plugins: ['*']
94 | })
95 | path.replaceWith(transformed.program.body[0])
96 | }
97 | }
98 | })
99 | }
100 | }
101 |
102 | }
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/src/jsx.js:
--------------------------------------------------------------------------------
1 | import Renderer from './renderer'
2 |
3 | import { escapeXml as esc } from 'commonmark/lib/common'
4 |
5 | let reUnsafeProtocol = /^javascript:|vbscript:|file:|data:/i
6 | let reSafeDataProtocol = /^data:image\/(?:png|gif|jpeg|webp)/i
7 |
8 | let potentiallyUnsafe = url =>
9 | reUnsafeProtocol.test(url) &&
10 | !reSafeDataProtocol.test(url)
11 |
12 |
13 | export default class JSXRenderer extends Renderer {
14 | constructor(options) {
15 | super(options)
16 | options = options || {}
17 | // by default, soft breaks are rendered as newlines in HTML
18 | options.softbreak = options.softbreak || '\n'
19 | // set to "
" to make them hard breaks
20 | // set to " " if you want to ignore line wrapping in source
21 |
22 | this.disableTags = 0
23 | this.lastOut = '\n'
24 | this.options = options
25 | }
26 |
27 | tag(name, attrs, selfclosing) {
28 | if (this.disableTags > 0) {
29 | return
30 | }
31 | this.buffer += ('<' + name)
32 | if (attrs && attrs.length > 0) {
33 | let i = 0
34 | let attrib
35 | while ((attrib = attrs[i]) !== undefined) {
36 | this.buffer += (' ' + attrib[0] + '="' + attrib[1] + '"')
37 | i++
38 | }
39 | }
40 | if (selfclosing) {
41 | this.buffer += ' /'
42 | }
43 | this.buffer += '>'
44 | this.lastOut = '>'
45 | }
46 |
47 |
48 | /* Node methods */
49 |
50 | text(node) {
51 | this.out(node.literal)
52 | }
53 |
54 | softbreak() {
55 | this.lit(this.options.softbreak)
56 | }
57 |
58 | linebreak() {
59 | this.tag('_m_.br', [], true)
60 | this.cr()
61 | }
62 |
63 | link(node, entering) {
64 | let attrs = this.attrs(node)
65 | if (entering) {
66 | if (!(this.options.safe && potentiallyUnsafe(node.destination))) {
67 | attrs.push([ 'href', esc(node.destination, true) ])
68 | }
69 | if (node.title) {
70 | attrs.push([ 'title', esc(node.title, true) ])
71 | }
72 | this.tag('_m_.a', attrs)
73 | } else {
74 | this.tag('/_m_.a')
75 | }
76 | }
77 |
78 | image(node, entering) {
79 | if (entering) {
80 | if (this.disableTags === 0) {
81 | if (this.options.safe && potentiallyUnsafe(node.destination)) {
82 | this.lit('<_m_.img src="" alt="')
83 | } else {
84 | this.lit('<_m_.img src="' + esc(node.destination, true) +
85 | '" alt="')
86 | }
87 | }
88 | this.disableTags += 1
89 | } else {
90 | this.disableTags -= 1
91 | if (this.disableTags === 0) {
92 | if (node.title) {
93 | this.lit('" title="' + esc(node.title, true))
94 | }
95 | this.lit('" />')
96 | }
97 | }
98 | }
99 |
100 | emph(node, entering) {
101 | this.tag(entering ? '_m_.em' : '/_m_.em')
102 | }
103 |
104 | strong(node, entering) {
105 | this.tag(entering ? '_m_.strong' : '/_m_.strong')
106 | }
107 |
108 | paragraph(node, entering) {
109 | let grandparent = node.parent.parent
110 | , attrs = this.attrs(node)
111 | if (grandparent !== null &&
112 | grandparent.type === 'list') {
113 | if (grandparent.listTight) {
114 | return
115 | }
116 | }
117 | if (entering) {
118 | this.cr()
119 | this.tag('_m_.p', attrs)
120 | } else {
121 | this.tag('/_m_.p')
122 | this.cr()
123 | }
124 | }
125 |
126 | heading(node, entering) {
127 | let tagname = '_m_.h' + node.level
128 | , attrs = this.attrs(node)
129 | if (entering) {
130 | this.cr()
131 | this.tag(tagname, attrs)
132 | } else {
133 | this.tag('/' + tagname)
134 | this.cr()
135 | }
136 | }
137 |
138 | code(node) {
139 | this.tag('_m_.code')
140 | this.out(node.literal)
141 | this.tag('/_m_.code')
142 | }
143 |
144 | code_block(node) {
145 |
146 | let info_words = node.info ? node.info.split(/\s+/) : []
147 | , attrs = this.attrs(node)
148 | if (info_words.length > 0 && info_words[0].length > 0) {
149 | attrs.push([ 'className', 'language-' + esc(info_words[0], true) ])
150 | }
151 | this.cr()
152 | this.tag('_m_.pre')
153 | this.tag('_m_.code', attrs)
154 | this.out(`{\`${node.literal}\`}`)
155 | this.tag('/_m_.code')
156 | this.tag('/_m_.pre')
157 | this.cr()
158 | }
159 |
160 | thematic_break(node) {
161 | let attrs = this.attrs(node)
162 | this.cr()
163 | this.tag('_m_.hr', attrs, true)
164 | this.cr()
165 | }
166 |
167 | block_quote(node, entering) {
168 | let attrs = this.attrs(node)
169 | if (entering) {
170 | this.cr()
171 | this.tag('_m_.blockquote', attrs)
172 | this.cr()
173 | } else {
174 | this.cr()
175 | this.tag('/_m_.blockquote')
176 | this.cr()
177 | }
178 | }
179 |
180 | list(node, entering) {
181 | let tagname = node.listType === 'bullet' ? 'ul' : 'ol'
182 | , attrs = this.attrs(node)
183 |
184 | if (entering) {
185 | let start = node.listStart
186 | if (start !== null && start !== 1) {
187 | attrs.push([ 'start', start.toString() ])
188 | }
189 | this.cr()
190 | this.tag('_m_.' + tagname, attrs)
191 | this.cr()
192 | } else {
193 | this.cr()
194 | this.tag('/_m_.' + tagname)
195 | this.cr()
196 | }
197 | }
198 |
199 | item(node, entering) {
200 | let attrs = this.attrs(node)
201 | if (entering) {
202 | this.tag('_m_.li', attrs)
203 | } else {
204 | this.tag('/_m_.li')
205 | this.cr()
206 | }
207 | }
208 |
209 | html_inline(node) {
210 | if (this.options.safe) {
211 | this.lit('')
212 | } else {
213 | this.lit(node.literal)
214 | }
215 | }
216 |
217 | html_block(node) {
218 | this.cr()
219 | if (this.options.safe) {
220 | this.lit('')
221 | } else {
222 | this.lit(node.literal)
223 | }
224 | this.cr()
225 | }
226 |
227 | custom_inline(node, entering) {
228 | if (entering && node.onEnter) {
229 | this.lit(node.onEnter)
230 | } else if (!entering && node.onExit) {
231 | this.lit(node.onExit)
232 | }
233 | }
234 |
235 | custom_block(node, entering) {
236 | this.cr()
237 | if (entering && node.onEnter) {
238 | this.lit(node.onEnter)
239 | } else if (!entering && node.onExit) {
240 | this.lit(node.onExit)
241 | }
242 | this.cr()
243 | }
244 |
245 | /* Helper methods */
246 |
247 | out(s) {
248 | this.lit(esc(s, false))
249 | }
250 |
251 | attrs(node) {
252 | let att = []
253 | if (this.options.sourcepos) {
254 | let pos = node.sourcepos
255 | if (pos) {
256 | att.push([ 'data-sourcepos', String(pos[0][0]) + ':' +
257 | String(pos[0][1]) + '-' + String(pos[1][0]) + ':' +
258 | String(pos[1][1]) ])
259 | }
260 | }
261 | return att
262 | }
263 |
264 | }
265 |
266 |
--------------------------------------------------------------------------------
/examples/prism.js:
--------------------------------------------------------------------------------
1 | /* http://prismjs.com/download.html?themes=prism&languages=markup+css+clike+javascript+jsx */
2 | var _self="undefined"!=typeof window?window:"undefined"!=typeof WorkerGlobalScope&&self instanceof WorkerGlobalScope?self:{},Prism=function(){var e=/\blang(?:uage)?-(\w+)\b/i,t=0,n=_self.Prism={util:{encode:function(e){return e instanceof a?new a(e.type,n.util.encode(e.content),e.alias):"Array"===n.util.type(e)?e.map(n.util.encode):e.replace(/&/g,"&").replace(/e.length)break e;if(!(v instanceof a)){u.lastIndex=0;var b=u.exec(v),k=1;if(!b&&h&&m!=r.length-1){if(u.lastIndex=y,b=u.exec(e),!b)break;for(var w=b.index+(c?b[1].length:0),_=b.index+b[0].length,A=m,P=y,j=r.length;j>A&&_>P;++A)P+=r[A].length,w>=P&&(++m,y=P);if(r[m]instanceof a||r[A-1].greedy)continue;k=A-m,v=e.slice(y,P),b.index-=y}if(b){c&&(f=b[1].length);var w=b.index+f,b=b[0].slice(f),_=w+b.length,O=v.slice(0,w),x=v.slice(_),S=[m,k];O&&S.push(O);var N=new a(l,g?n.tokenize(b,g):b,d,b,h);S.push(N),x&&S.push(x),Array.prototype.splice.apply(r,S)}}}}}return r},hooks:{all:{},add:function(e,t){var a=n.hooks.all;a[e]=a[e]||[],a[e].push(t)},run:function(e,t){var a=n.hooks.all[e];if(a&&a.length)for(var r,i=0;r=a[i++];)r(t)}}},a=n.Token=function(e,t,n,a,r){this.type=e,this.content=t,this.alias=n,this.length=0|(a||"").length,this.greedy=!!r};if(a.stringify=function(e,t,r){if("string"==typeof e)return e;if("Array"===n.util.type(e))return e.map(function(n){return a.stringify(n,t,e)}).join("");var i={type:e.type,content:a.stringify(e.content,t,r),tag:"span",classes:["token",e.type],attributes:{},language:t,parent:r};if("comment"==i.type&&(i.attributes.spellcheck="true"),e.alias){var l="Array"===n.util.type(e.alias)?e.alias:[e.alias];Array.prototype.push.apply(i.classes,l)}n.hooks.run("wrap",i);var o=Object.keys(i.attributes).map(function(e){return e+'="'+(i.attributes[e]||"").replace(/"/g,""")+'"'}).join(" ");return"<"+i.tag+' class="'+i.classes.join(" ")+'"'+(o?" "+o:"")+">"+i.content+""+i.tag+">"},!_self.document)return _self.addEventListener?(_self.addEventListener("message",function(e){var t=JSON.parse(e.data),a=t.language,r=t.code,i=t.immediateClose;_self.postMessage(n.highlight(r,n.languages[a],a)),i&&_self.close()},!1),_self.Prism):_self.Prism;var r=document.currentScript||[].slice.call(document.getElementsByTagName("script")).pop();return r&&(n.filename=r.src,document.addEventListener&&!r.hasAttribute("data-manual")&&("loading"!==document.readyState?window.requestAnimationFrame?window.requestAnimationFrame(n.highlightAll):window.setTimeout(n.highlightAll,16):document.addEventListener("DOMContentLoaded",n.highlightAll))),_self.Prism}();"undefined"!=typeof module&&module.exports&&(module.exports=Prism),"undefined"!=typeof global&&(global.Prism=Prism);
3 | Prism.languages.markup={comment://,prolog:/<\?[\w\W]+?\?>/,doctype://i,cdata://i,tag:{pattern:/<\/?(?!\d)[^\s>\/=$<]+(?:\s+[^\s>\/=]+(?:=(?:("|')(?:\\\1|\\?(?!\1)[\w\W])*\1|[^\s'">=]+))?)*\s*\/?>/i,inside:{tag:{pattern:/^<\/?[^\s>\/]+/i,inside:{punctuation:/^<\/?/,namespace:/^[^\s>\/:]+:/}},"attr-value":{pattern:/=(?:('|")[\w\W]*?(\1)|[^\s>]+)/i,inside:{punctuation:/[=>"']/}},punctuation:/\/?>/,"attr-name":{pattern:/[^\s>\/]+/,inside:{namespace:/^[^\s>\/:]+:/}}}},entity:/?[\da-z]{1,8};/i},Prism.hooks.add("wrap",function(a){"entity"===a.type&&(a.attributes.title=a.content.replace(/&/,"&"))}),Prism.languages.xml=Prism.languages.markup,Prism.languages.html=Prism.languages.markup,Prism.languages.mathml=Prism.languages.markup,Prism.languages.svg=Prism.languages.markup;
4 | Prism.languages.css={comment:/\/\*[\w\W]*?\*\//,atrule:{pattern:/@[\w-]+?.*?(;|(?=\s*\{))/i,inside:{rule:/@[\w-]+/}},url:/url\((?:(["'])(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1|.*?)\)/i,selector:/[^\{\}\s][^\{\};]*?(?=\s*\{)/,string:{pattern:/("|')(\\(?:\r\n|[\w\W])|(?!\1)[^\\\r\n])*\1/,greedy:!0},property:/(\b|\B)[\w-]+(?=\s*:)/i,important:/\B!important\b/i,"function":/[-a-z0-9]+(?=\()/i,punctuation:/[(){};:]/},Prism.languages.css.atrule.inside.rest=Prism.util.clone(Prism.languages.css),Prism.languages.markup&&(Prism.languages.insertBefore("markup","tag",{style:{pattern:/(