├── src ├── index.js ├── plugins │ └── markdown │ │ ├── index.js │ │ ├── converters.js │ │ └── withMarkdown.jsx └── Editor.jsx ├── docs ├── src │ ├── screens │ │ ├── main │ │ │ ├── index.js │ │ │ ├── main.css │ │ │ └── main.jsx │ │ ├── html-editor │ │ │ ├── index.js │ │ │ ├── html-editor.css │ │ │ └── html-editor.jsx │ │ └── markdown-editor │ │ │ ├── index.js │ │ │ ├── markdown-editor.css │ │ │ └── markdown-editor.jsx │ ├── components │ │ ├── code │ │ │ ├── index.js │ │ │ ├── code.css │ │ │ └── code.jsx │ │ ├── navbar │ │ │ ├── index.js │ │ │ ├── navbar.css │ │ │ └── navbar.jsx │ │ └── blockquote │ │ │ ├── index.js │ │ │ ├── blockquote.css │ │ │ └── blockquote.jsx │ ├── index.jsx │ └── index.css ├── .gitignore ├── index.html ├── config │ └── webpack.config.js └── package.json ├── .gitignore ├── config ├── rollup.editor.js └── rollup.markdown.js ├── LICENSE ├── package.json ├── README.md └── plugins └── markdown.js /src/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './Editor' 2 | -------------------------------------------------------------------------------- /docs/src/screens/main/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './main' 2 | -------------------------------------------------------------------------------- /docs/src/components/code/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './code' 2 | -------------------------------------------------------------------------------- /docs/src/components/navbar/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './navbar' 2 | -------------------------------------------------------------------------------- /docs/src/screens/main/main.css: -------------------------------------------------------------------------------- 1 | .content { 2 | padding: 20px 0; 3 | } 4 | -------------------------------------------------------------------------------- /docs/src/components/blockquote/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './blockquote' 2 | -------------------------------------------------------------------------------- /docs/src/screens/html-editor/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './html-editor' 2 | -------------------------------------------------------------------------------- /docs/src/screens/markdown-editor/index.js: -------------------------------------------------------------------------------- 1 | export { default } from './markdown-editor' 2 | -------------------------------------------------------------------------------- /src/plugins/markdown/index.js: -------------------------------------------------------------------------------- 1 | import Editor from '../../Editor' 2 | import withMarkdown from './withMarkdown.jsx' 3 | 4 | export default withMarkdown(Editor) 5 | -------------------------------------------------------------------------------- /docs/.gitignore: -------------------------------------------------------------------------------- 1 | # dependency directory 2 | node_modules/ 3 | 4 | # coverage directory 5 | coverage/ 6 | 7 | # logs 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # misc 13 | *.DS_Store 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # dependency directory 2 | node_modules/ 3 | 4 | # coverage directory 5 | coverage/ 6 | 7 | # logs 8 | npm-debug.log* 9 | yarn-debug.log* 10 | yarn-error.log* 11 | 12 | # misc 13 | *.DS_Store 14 | .vscode/ 15 | -------------------------------------------------------------------------------- /docs/src/components/blockquote/blockquote.css: -------------------------------------------------------------------------------- 1 | blockquote { 2 | margin: 0; 3 | } 4 | 5 | .blockquote { 6 | padding: 0 1em; 7 | color: #6a737d; 8 | border-left: 0.25em solid #dfe2e5; 9 | margin-top: 0; 10 | margin-bottom: 16px; 11 | } -------------------------------------------------------------------------------- /docs/src/screens/html-editor/html-editor.css: -------------------------------------------------------------------------------- 1 | .htmlEditor { 2 | display: flex; 3 | justify-content: space-between; 4 | } 5 | 6 | .htmlEditor > div { 7 | width: 50%; 8 | flex: 0 0 50%; 9 | } 10 | 11 | .htmlEditor_code { 12 | margin-left: 20px; 13 | } -------------------------------------------------------------------------------- /docs/src/screens/markdown-editor/markdown-editor.css: -------------------------------------------------------------------------------- 1 | .markdownEditor { 2 | display: flex; 3 | justify-content: space-between; 4 | } 5 | 6 | .markdownEditor > div { 7 | width: 50%; 8 | flex: 0 0 50%; 9 | } 10 | 11 | .markdownEditor_code { 12 | margin-left: 20px; 13 | } -------------------------------------------------------------------------------- /docs/src/index.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDom from 'react-dom' 3 | 4 | import 'highlight.js/styles/github-gist.css' 5 | import './index.css' 6 | import Navbar from './components/navbar' 7 | import Main from './screens/main' 8 | 9 | const App = () => { 10 | return ( 11 |
12 | 13 |
14 |
15 | ) 16 | } 17 | 18 | ReactDom.render(, document.getElementById('root')) 19 | -------------------------------------------------------------------------------- /src/plugins/markdown/converters.js: -------------------------------------------------------------------------------- 1 | export default [ 2 | // replace div 3 | { 4 | filter: 'div', 5 | replacement: content => `\n${content}`, 6 | }, 7 | // not managed in markdown 8 | { 9 | filter: ['u', 'span'], 10 | replacement: content => content, 11 | }, 12 | // Fenced code blocks 13 | { 14 | filter: node => node.nodeName === 'PRE', 15 | replacement: content => `\n\n\`\`\`\n${content}\n\`\`\`\n\n`, 16 | }, 17 | ] 18 | -------------------------------------------------------------------------------- /docs/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font-family: 'Ubuntu', sans-serif; 3 | font-size: 1em; 4 | margin: 0; 5 | padding: 0; 6 | line-height: 1.4; 7 | height: 100%; 8 | } 9 | 10 | .container { 11 | width: 980px; 12 | margin-right: auto; 13 | margin-left: auto; 14 | } 15 | 16 | .pell-container { 17 | border: 1px solid #ddd; 18 | border-radius: 3px; 19 | height: 300px; 20 | overflow: hidden; 21 | } 22 | 23 | .pell-content { 24 | height: calc(300px - 32px) !important; 25 | } -------------------------------------------------------------------------------- /docs/src/components/code/code.css: -------------------------------------------------------------------------------- 1 | .code { 2 | border: 1px solid #ddd; 3 | border-radius: 3px; 4 | height: 300px; 5 | overflow: hidden; 6 | } 7 | 8 | .code > pre { 9 | margin: 0; 10 | } 11 | 12 | .code > pre > code { 13 | white-space:pre-wrap; 14 | padding: 10px; 15 | height: calc(300px - 32px); 16 | } 17 | 18 | .code__title { 19 | padding-left: 10px; 20 | border-bottom: 1px solid #ddd; 21 | height: 32px; 22 | line-height: 32px; 23 | color: #AAA; 24 | font-size: .8em; 25 | } 26 | -------------------------------------------------------------------------------- /docs/src/components/navbar/navbar.css: -------------------------------------------------------------------------------- 1 | .navbar { 2 | height: 54px; 3 | line-height: 54px; 4 | color: #fff; 5 | background-color: #24292e; 6 | } 7 | 8 | .navbar__title { 9 | font-weight: 700; 10 | padding-right: 25px; 11 | float: left; 12 | } 13 | 14 | .navbar__menu { 15 | float: right; 16 | } 17 | 18 | .navbar__menu a { 19 | padding: 0 10px; 20 | text-decoration: none; 21 | color: rgba(255,255,255,0.75); 22 | } 23 | 24 | .navbar__menu a:visited { 25 | color: rgba(255,255,255,0.75); 26 | } 27 | 28 | .navbar__menu a:hover { 29 | color: #fff; 30 | } 31 | -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | react-pell demos 7 | 8 | 9 | 10 | 13 |
14 | react-pell demos. WIP 15 |
16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/src/screens/main/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | 3 | import './main.css' 4 | import Blockquote from '../../components/blockquote' 5 | import HtmlEditor from '../html-editor' 6 | import MarkdownEditor from '../markdown-editor' 7 | 8 | const Main = () => { 9 | return ( 10 |
11 |
12 | 13 | 14 |

More samples coming soon...

15 |
16 | ) 17 | } 18 | 19 | export default Main 20 | -------------------------------------------------------------------------------- /docs/src/components/blockquote/blockquote.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import c from 'classnames' 4 | 5 | import './blockquote.css' 6 | 7 | const Blockquote = ({ text, className, style }) => { 8 | return ( 9 |
10 |

11 | {text} 12 |

13 |
14 | ) 15 | } 16 | 17 | Blockquote.propTypes = { 18 | text: PropTypes.node, 19 | className: PropTypes.string, 20 | style: PropTypes.object, 21 | } 22 | 23 | Blockquote.defaultProps = { 24 | text: '', 25 | className: undefined, 26 | style: undefined, 27 | } 28 | 29 | export default Blockquote 30 | -------------------------------------------------------------------------------- /docs/src/components/code/code.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import c from 'classnames' 4 | import Highlight from 'react-highlight' 5 | 6 | import './code.css' 7 | 8 | const Code = ({ title, language, content, className }) => { 9 | return ( 10 |
11 |
12 | {title} 13 |
14 | 15 | {content} 16 | 17 |
18 | ) 19 | } 20 | 21 | Code.propTypes = { 22 | title: PropTypes.string, 23 | language: PropTypes.string.isRequired, 24 | content: PropTypes.string, 25 | className: PropTypes.string, 26 | } 27 | 28 | Code.defaultProps = { 29 | title: undefined, 30 | content: '', 31 | className: undefined, 32 | } 33 | 34 | export default Code 35 | -------------------------------------------------------------------------------- /docs/src/components/navbar/navbar.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import PropTypes from 'prop-types' 3 | import c from 'classnames' 4 | 5 | import './navbar.css' 6 | 7 | const Navbar = ({ className }) => { 8 | return ( 9 |
10 |
11 |
react-pell samples
12 | 21 |
22 |
23 | ) 24 | } 25 | 26 | Navbar.propTypes = { 27 | className: PropTypes.string, 28 | } 29 | 30 | Navbar.defaultProps = { 31 | className: undefined, 32 | } 33 | 34 | export default Navbar 35 | -------------------------------------------------------------------------------- /config/rollup.editor.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import babel from 'rollup-plugin-babel' 3 | import nodeResolve from 'rollup-plugin-node-resolve' 4 | import commonjs from 'rollup-plugin-commonjs' 5 | import postcss from 'rollup-plugin-postcss' 6 | import uglify from 'rollup-plugin-uglify' 7 | 8 | const pkg = JSON.parse(fs.readFileSync('./package.json')) 9 | 10 | export default { 11 | entry: pkg['jsnext:main'] || 'src/index.js', 12 | dest: pkg.main, 13 | sourceMap: false, 14 | moduleName: pkg.amdName || pkg.name, 15 | format: process.env.FORMAT || 'umd', 16 | external: [...Object.keys(pkg.peerDependencies)], 17 | globals: { 18 | react: 'React', 19 | 'prop-types': 'PropTypes', 20 | }, 21 | plugins: [ 22 | nodeResolve({ 23 | extensions: ['.js', '.jsx'], 24 | }), 25 | commonjs({ 26 | include: 'node_modules/**', 27 | }), 28 | babel({ 29 | exclude: 'node_modules/**', 30 | }), 31 | uglify({ 32 | output: { 33 | comments: false, 34 | }, 35 | }), 36 | postcss({ 37 | extensions: ['.css'], 38 | }), 39 | ], 40 | } 41 | -------------------------------------------------------------------------------- /config/rollup.markdown.js: -------------------------------------------------------------------------------- 1 | import fs from 'fs' 2 | import babel from 'rollup-plugin-babel' 3 | import nodeResolve from 'rollup-plugin-node-resolve' 4 | import commonjs from 'rollup-plugin-commonjs' 5 | import postcss from 'rollup-plugin-postcss' 6 | import uglify from 'rollup-plugin-uglify' 7 | 8 | const pkg = JSON.parse(fs.readFileSync('./package.json')) 9 | 10 | export default { 11 | entry: 'src/plugins/markdown/index.js', 12 | dest: 'plugins/markdown.js', 13 | sourceMap: false, 14 | moduleName: pkg.amdName || pkg.name, 15 | format: process.env.FORMAT || 'umd', 16 | external: [...Object.keys(pkg.peerDependencies)], 17 | globals: { 18 | react: 'React', 19 | 'prop-types': 'PropTypes', 20 | }, 21 | plugins: [ 22 | nodeResolve({ 23 | extensions: ['.js', '.jsx'], 24 | }), 25 | commonjs({ 26 | include: 'node_modules/**', 27 | }), 28 | babel({ 29 | exclude: 'node_modules/**', 30 | }), 31 | uglify({ 32 | output: { 33 | comments: false, 34 | }, 35 | }), 36 | postcss({ 37 | extensions: ['.css'], 38 | }), 39 | ], 40 | } 41 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2017 bpetetot/react-pell 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /src/plugins/markdown/withMarkdown.jsx: -------------------------------------------------------------------------------- 1 | /* eslint react/prop-types: 0 */ 2 | import React, { Component } from 'react' 3 | import showdown from 'showdown' 4 | import toMarkdown from 'to-markdown/dist/to-markdown' 5 | import defaultConverters from './converters' 6 | 7 | const markdownToHtml = (markdown, gfm) => { 8 | const convertor = new showdown.Converter() 9 | if (gfm) { 10 | showdown.setFlavor('github') 11 | } 12 | return convertor.makeHtml(markdown) 13 | } 14 | 15 | const htmlToMarkdown = (html, converters, gfm) => 16 | toMarkdown(html, { converters: [...defaultConverters, ...converters], gfm }) 17 | 18 | export default Editor => 19 | class extends Component { 20 | handleMarkdownChange = (html) => { 21 | const { converters = [], gfm = true } = this.props 22 | this.props.onChange(htmlToMarkdown(html, converters, gfm)) 23 | } 24 | 25 | // return the editor content 26 | getContent = () => { 27 | const { converters = [], gfm = true } = this.props 28 | return htmlToMarkdown(this.editor.getContent(), converters, gfm) 29 | } 30 | 31 | render() { 32 | return ( 33 | (this.editor = e)} 35 | {...this.props} 36 | styleWithCSS={false} 37 | defaultContent={markdownToHtml(this.props.defaultContent, this.props.gfm)} 38 | onChange={this.handleMarkdownChange} 39 | /> 40 | ) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /docs/src/screens/html-editor/html-editor.jsx: -------------------------------------------------------------------------------- 1 | /* eslint react/no-did-mount-set-state: 0 */ 2 | import React, { Component } from 'react' 3 | 4 | import Editor from 'react-pell' 5 | import Code from '../../components/code' 6 | 7 | import './html-editor.css' 8 | 9 | const defaultContent = 10 | '

Hello world

Try the awesome pell HTML editor with react. 🦄

You just have to :
import Editor from \'react-pell\'
<Editor onChange="this.handleChange" />


' 11 | 12 | class HtmlEditor extends Component { 13 | state = { 14 | content: undefined, 15 | } 16 | 17 | componentDidMount() { 18 | this.setState(() => ({ content: this.editor.getContent() })) 19 | } 20 | 21 | handleChange = (html) => { 22 | this.setState(() => ({ content: html })) 23 | } 24 | 25 | render() { 26 | return ( 27 |
28 |

HTML editor

29 |
30 | (this.editor = e)} 32 | onChange={this.handleChange} 33 | defaultContent={defaultContent} 34 | /> 35 | 41 |
42 |
43 | ) 44 | } 45 | } 46 | 47 | export default HtmlEditor 48 | -------------------------------------------------------------------------------- /docs/config/webpack.config.js: -------------------------------------------------------------------------------- 1 | const path = require('path') 2 | const webpack = require('webpack') 3 | 4 | const dev = process.env.NODE_ENV !== 'production' 5 | 6 | function getEntrySources(sources) { 7 | if (dev) { 8 | sources.push('webpack-dev-server/client?http://localhost:3000') 9 | sources.push('webpack/hot/only-dev-server') 10 | } 11 | return sources 12 | } 13 | 14 | function getLoaders(loaders) { 15 | if (dev) { 16 | loaders.push('react-hot-loader') 17 | } 18 | loaders.push('babel-loader') 19 | return loaders 20 | } 21 | 22 | function getPlugins(plugins) { 23 | if (dev) { 24 | plugins.push(new webpack.HotModuleReplacementPlugin()) 25 | } 26 | return plugins 27 | } 28 | 29 | module.exports = { 30 | devtool: dev ? 'eval' : '', 31 | entry: { 32 | examples: getEntrySources(['./src/index.jsx']), 33 | }, 34 | output: { 35 | path: path.join(__dirname, '..', 'build'), 36 | filename: 'bundle.js', 37 | publicPath: '/build', 38 | }, 39 | resolve: { 40 | modules: [path.resolve('./src'), 'node_modules'], 41 | extensions: ['.js', '.jsx'], 42 | }, 43 | plugins: getPlugins([]), 44 | module: { 45 | rules: [ 46 | { 47 | test: /\.jsx?$/, 48 | use: getLoaders([]), 49 | include: [path.join(__dirname, '..', 'src')], 50 | }, 51 | { 52 | test: /\.css$/, 53 | use: ['style-loader', 'css-loader'], 54 | }, 55 | { 56 | test: /\.(png|svg|gif|jpg|html)$/, 57 | use: ['file-loader'], 58 | }, 59 | ], 60 | }, 61 | } 62 | -------------------------------------------------------------------------------- /docs/src/screens/markdown-editor/markdown-editor.jsx: -------------------------------------------------------------------------------- 1 | /* eslint react/no-did-mount-set-state: 0 */ 2 | import React, { Component } from 'react' 3 | 4 | import Editor from 'react-pell/plugins/markdown' 5 | import Code from '../../components/code' 6 | 7 | import './markdown-editor.css' 8 | 9 | const defaultContent = 10 | ` 11 | ## Hello world 12 | 13 | Try the awesome **pell editor** with react. 14 | It can produce markdown 🤘 15 | 16 | You just have to : 17 | 18 | \`\`\` 19 | import Editor from 'react-pell/plugins/markdown' 20 | \`\`\` 21 | 22 | \`\`\` 23 | 24 | \`\`\` 25 | ` 26 | 27 | 28 | class MarkdownEditor extends Component { 29 | state = { 30 | content: undefined, 31 | } 32 | 33 | componentDidMount() { 34 | this.setState(() => ({ content: this.editor.getContent() })) 35 | } 36 | 37 | handleChange = (markdown) => { 38 | this.setState(() => ({ content: markdown })) 39 | } 40 | 41 | render() { 42 | return ( 43 |
44 |

Markdown editor

45 |
46 | (this.editor = e)} 48 | onChange={this.handleChange} 49 | defaultContent={defaultContent} 50 | actions={['heading1', 'heading2', 'bold', 'italic', 'quote', 'code', 'link', 'olist', 'ulist']} 51 | /> 52 | 58 |
59 |
60 | ) 61 | } 62 | } 63 | 64 | export default MarkdownEditor 65 | -------------------------------------------------------------------------------- /docs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-pell-samples", 3 | "version": "0.1.0", 4 | "description": "samples for react-pell lib", 5 | "main": "index.html", 6 | "author": "Benjamin Petetot ", 7 | "license": "MIT", 8 | "scripts": { 9 | "lint": "eslint --ext js,jsx src", 10 | "start": "cross-env NODE_ENV=development && webpack-dev-server --inline --hot --port=3000 --history-api-fallback --config ./config/webpack.config.js", 11 | "build": "cross-env NODE_ENV=production webpack -p --config ./config/webpack.config.js" 12 | }, 13 | "dependencies": { 14 | "classnames": "^2.2.5", 15 | "prop-types": "^15.5.10", 16 | "react": "^15.6.1", 17 | "react-dom": "^15.6.1", 18 | "react-highlight": "^0.10.0", 19 | "react-pell": "file:../" 20 | }, 21 | "devDependencies": { 22 | "babel-cli": "^6.24.1", 23 | "babel-core": "^6.25.0", 24 | "babel-eslint": "^7.2.3", 25 | "babel-loader": "^7.1.1", 26 | "babel-plugin-transform-class-properties": "^6.24.1", 27 | "babel-plugin-transform-object-rest-spread": "^6.23.0", 28 | "babel-preset-react": "^6.24.1", 29 | "cross-env": "^5.0.1", 30 | "css-loader": "^0.28.4", 31 | "eslint": "^3.19.0", 32 | "eslint-config-airbnb": "^15.0.2", 33 | "eslint-plugin-import": "^2.7.0", 34 | "eslint-plugin-jsx-a11y": "^5.1.1", 35 | "eslint-plugin-react": "^7.1.0", 36 | "file-loader": "^0.11.2", 37 | "react-hot-loader": "^1.3.1", 38 | "style-loader": "^0.18.2", 39 | "webpack": "^3.3.0", 40 | "webpack-dev-server": "^2.5.1" 41 | }, 42 | "babel": { 43 | "presets": [ 44 | "es2015", 45 | "react" 46 | ], 47 | "plugins": [ 48 | "transform-class-properties", 49 | "transform-object-rest-spread" 50 | ] 51 | }, 52 | "eslintConfig": { 53 | "parser": "babel-eslint", 54 | "extends": [ 55 | "airbnb" 56 | ], 57 | "env": { 58 | "browser": true 59 | }, 60 | "rules": { 61 | "semi": [ 62 | 2, 63 | "never" 64 | ], 65 | "import/no-extraneous-dependencies": 0, 66 | "import/no-unresolved": 0, 67 | "import/extensions": 0, 68 | "react/forbid-prop-types": 0, 69 | "arrow-body-style": 0 70 | } 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-pell", 3 | "version": "0.4.0", 4 | "description": "React component wrapping a HTML and Markdown editor based on the wysiwyg pell editor", 5 | "main": "build/index.js", 6 | "repository": "https://github.com/bpetetot/react-pell.git", 7 | "author": "Benjamin Petetot ", 8 | "keywords": ["react", "pell", "editor", "html", "markdown", "wysiwyg"], 9 | "license": "MIT", 10 | "scripts": { 11 | "lint": "eslint --ext js,jsx src", 12 | "build:editor": "cross-env NODE_ENV=build rollup -c ./config/rollup.editor.js", 13 | "build:markdown": "cross-env NODE_ENV=build rollup -c ./config/rollup.markdown.js", 14 | "build": "npm-run-all --parallel build:*" 15 | }, 16 | "prepack": "yarn build", 17 | "dependencies": { 18 | "pell": "^0.7.0", 19 | "showdown": "^1.7.1", 20 | "to-markdown": "^3.1.0" 21 | }, 22 | "peerDependencies": { 23 | "prop-types": "^15.5.10", 24 | "react": "^15.6.1" 25 | }, 26 | "devDependencies": { 27 | "babel-cli": "^6.24.1", 28 | "babel-eslint": "^7.2.3", 29 | "babel-plugin-external-helpers": "^6.22.0", 30 | "babel-plugin-transform-class-properties": "^6.24.1", 31 | "babel-plugin-transform-object-rest-spread": "^6.23.0", 32 | "babel-preset-es2015-rollup": "^3.0.0", 33 | "babel-preset-react": "^6.24.1", 34 | "cross-env": "^5.0.1", 35 | "eslint": "^3.19.0", 36 | "eslint-config-airbnb": "^15.0.2", 37 | "eslint-plugin-import": "^2.7.0", 38 | "eslint-plugin-jsx-a11y": "^5.1.1", 39 | "eslint-plugin-react": "^7.1.0", 40 | "npm-run-all": "^4.0.2", 41 | "rollup": "^0.45.2", 42 | "rollup-plugin-babel": "^2.7.1", 43 | "rollup-plugin-commonjs": "^8.0.2", 44 | "rollup-plugin-node-resolve": "^3.0.0", 45 | "rollup-plugin-postcss": "^0.5.3", 46 | "rollup-plugin-uglify": "^2.0.1" 47 | }, 48 | "babel": { 49 | "presets": [ 50 | "es2015-rollup", 51 | "react" 52 | ], 53 | "plugins": [ 54 | "external-helpers", 55 | "transform-class-properties", 56 | "transform-object-rest-spread" 57 | ] 58 | }, 59 | "eslintConfig": { 60 | "parser": "babel-eslint", 61 | "extends": [ 62 | "airbnb" 63 | ], 64 | "rules": { 65 | "semi": [ 66 | 2, 67 | "never" 68 | ], 69 | "import/no-extraneous-dependencies": 0, 70 | "import/no-unresolved": 0, 71 | "import/extensions": 0, 72 | "react/forbid-prop-types": 0 73 | } 74 | } 75 | } 76 | -------------------------------------------------------------------------------- /src/Editor.jsx: -------------------------------------------------------------------------------- 1 | import React, { Component } from 'react' 2 | import PropTypes from 'prop-types' 3 | import pell from 'pell' 4 | 5 | import 'pell/dist/pell.min.css' 6 | 7 | // pass pell object to custom actions callback 8 | const mapActions = (actions) => { 9 | if (actions) { 10 | return actions.map((e) => { 11 | if (typeof e === 'object' && e.result) { 12 | return { ...e, result: () => e.result(pell) } 13 | } 14 | return e 15 | }) 16 | } 17 | return actions 18 | } 19 | 20 | class Editor extends Component { 21 | componentDidMount() { 22 | const { 23 | onChange, 24 | actions, 25 | styleWithCSS, 26 | actionBarClass, 27 | buttonClass, 28 | contentClass, 29 | defaultContent, 30 | } = this.props 31 | 32 | // initialize pell editor 33 | pell.init({ 34 | element: this.container, 35 | onChange: html => onChange(html), 36 | actions: mapActions(actions), 37 | styleWithCSS, 38 | classes: { 39 | actionbar: actionBarClass, 40 | button: buttonClass, 41 | content: contentClass, 42 | }, 43 | }) 44 | 45 | // set default content 46 | this.container.content.innerHTML = defaultContent 47 | } 48 | 49 | componentDidUpdate() { 50 | const { defaultContent } = this.props 51 | if (this.container.content.innerHTML !== defaultContent) { 52 | this.container.content.innerHTML = defaultContent 53 | } 54 | } 55 | 56 | // return the editor content 57 | getContent = () => this.container.content.innerHTML 58 | 59 | render() { 60 | return
(this.container = e)} className={this.props.containerClass} /> 61 | } 62 | } 63 | 64 | Editor.propTypes = { 65 | onChange: PropTypes.func.isRequired, 66 | defaultContent: PropTypes.string, 67 | actions: PropTypes.array, 68 | styleWithCSS: PropTypes.bool, 69 | containerClass: PropTypes.string, 70 | actionBarClass: PropTypes.string, 71 | buttonClass: PropTypes.string, 72 | contentClass: PropTypes.string, 73 | } 74 | 75 | Editor.defaultProps = { 76 | defaultContent: '', 77 | styleWithCSS: false, 78 | actions: [ 79 | 'bold', 80 | 'italic', 81 | 'underline', 82 | 'strikethrough', 83 | 'heading1', 84 | 'heading2', 85 | 'olist', 86 | 'ulist', 87 | 'quote', 88 | 'code', 89 | 'line', 90 | ], 91 | containerClass: 'pell-container', 92 | actionBarClass: 'pell-actionbar', 93 | buttonClass: 'pell-button', 94 | contentClass: 'pell-content', 95 | } 96 | 97 | export default Editor 98 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # react-pell 2 | 3 | > React component wrapping a HTML and Markdown editor based on [the wysiwyg pell editor](https://github.com/jaredreich/pell) 4 | 5 | ## Contents 6 | - [Getting started](#getting-started) 7 | - [HTML Editor](#html-editor) 8 | - [Markdown Editor](#markdown-editor) 9 | 10 | ## Getting started 11 | 12 | Install react-pell as dependency : 13 | ``` 14 | yarn add react-pell 15 | ``` 16 | 17 | Basic example : 18 | ```es6 19 | import React, { Component } from 'react'; 20 | import Editor from 'react-pell' 21 | 22 | class App extends Component { 23 | 24 | handleChange(html) { 25 | console.log(html) 26 | } 27 | 28 | render() { 29 | return ( 30 | 31 | ); 32 | } 33 | } 34 | 35 | export default App; 36 | ``` 37 | 38 | ## HTML editor 39 | 40 | A basic wrapping of the [pell editor](https://github.com/jaredreich/pell). 41 | 42 | **Import the Editor :** 43 | ```es6 44 | import Editor from 'react-pell' 45 | ``` 46 | 47 | **Example :** 48 | ```es6 49 | 55 | ``` 56 | 57 | **Prop types :** 58 | 59 | property | type | required | description 60 | ----------|------|----------|------------ 61 | onChange | func | true | function handling changes from the editor, the output html is the first parameter 62 | defaultContent | string | false | default content of the editor (HTML serialized in a string) 63 | actions | array | false | table of available actions or custom actions in the editor ([watch pell documentation for more details](https://github.com/jaredreich/pell/blob/master/README.md)) **Note:** `pell` instance is given at first parameter of `result` callback of custom actions. (default: `['bold', 'italic', 'underline', 'strikethrough', 'heading1', 'heading2', 'olist', 'ulist', 'quote', 'code', 'line']`). 64 | styleWithCSS | bool | false | outputs styles with css instead of HTML elements (default: `false`) 65 | containerClass | string | false | class name of the container (default: `pell-container`) 66 | actionBarClass | string | false | class name of the action bar (default: `pell-actionbar`) 67 | buttonClass | string | false | class name of buttons (default: `pell-button`) 68 | contentClass | string | false | class name of the content (default: `pell-content`) 69 | 70 | ## Markdown editor 71 | 72 | This editor plugin extends the behaviour of [pell](https://github.com/jaredreich/pell) to edit markdown. 73 | We are using following libraries to manage markdown : 74 | - html to markdown : [`to-markdown`](https://github.com/domchristie/to-markdown) 75 | - markdown to html : [`showdown`](https://github.com/showdownjs/showdown) 76 | 77 | **Import the Editor :** 78 | ```es6 79 | import Editor from 'react-pell/plugins/markdown' 80 | ``` 81 | 82 | **Example :** 83 | ```es6 84 | 91 | ``` 92 | 93 | **Prop types :** 94 | 95 | Same props as the Basic HTML pell editor with following props added : 96 | 97 | property | type | required | description 98 | ----------|------|----------|------------ 99 | converters | array | false | converters used to handle custom convertions from HTML to markdown. [see to-markdown documentation for more details](https://github.com/domchristie/to-markdown/blob/master/README.md#converters-array) 100 | gmf | bool | true | beta support for GitHub flavored markdown (GFM) 101 | 102 | ## LICENSE 103 | 104 | MIT -------------------------------------------------------------------------------- /plugins/markdown.js: -------------------------------------------------------------------------------- 1 | !function(e,r){"object"==typeof exports&&"undefined"!=typeof module?module.exports=r(require("react"),require("prop-types")):"function"==typeof define&&define.amd?define(["react","prop-types"],r):e["react-pell"]=r(e.React,e.PropTypes)}(this,function(e,r){"use strict";function t(){throw new Error("Dynamic requires are not currently supported by rollup-plugin-commonjs")}function n(e,r){return r={exports:{}},e(r,r.exports),r.exports}var a="default"in e?e.default:e;r=r&&r.hasOwnProperty("default")?r.default:r;var o="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:{},i=function(e){return e&&e.__esModule?e.default:e}(n(function(e,r){!function(e,t){t(r)}(0,function(e){var r=Object.assign||function(e){for(var r=1;rB",title:"Bold",result:function(){return a("bold")}},italic:{icon:"I",title:"Italic",result:function(){return a("italic")}},underline:{icon:"U",title:"Underline",result:function(){return a("underline")}},strikethrough:{icon:"S",title:"Strike-through",result:function(){return a("strikeThrough")}},heading1:{icon:"H1",title:"Heading 1",result:function(){return a("formatBlock","

")}},heading2:{icon:"H2",title:"Heading 2",result:function(){return a("formatBlock","

")}},paragraph:{icon:"¶",title:"Paragraph",result:function(){return a("formatBlock","

")}},quote:{icon:"“ ”",title:"Quote",result:function(){return a("formatBlock","

")}},olist:{icon:"#",title:"Ordered List",result:function(){return a("insertOrderedList")}},ulist:{icon:"•",title:"Unordered List",result:function(){return a("insertUnorderedList")}},code:{icon:"</>",title:"Code",result:function(){return a("formatBlock","
")}},line:{icon:"―",title:"Horizontal Line",result:function(){return a("insertHorizontalRule")}},link:{icon:"🔗",title:"Link",result:function(){var e=window.prompt("Enter the link URL");e&&a("createLink",e)}},image:{icon:"📷",title:"Image",result:function(){var e=window.prompt("Enter the image URL");e&&a("insertImage",e)}}},n={actionbar:"pell-actionbar",button:"pell-button",content:"pell-content"},a=function(e){var r=arguments.length>1&&void 0!==arguments[1]?arguments[1]:null;document.execCommand(e,!1,r)},o=function(e){9===e.which&&e.preventDefault()},i=function(e){e.actions=e.actions?e.actions.map(function(e){return"string"==typeof e?t[e]:t[e.name]?r({},t[e.name],e):e}):Object.keys(t).map(function(e){return t[e]}),e.classes=r({},n,e.classes);var i=document.createElement("div");return i.className=e.classes.actionbar,e.element.appendChild(i),e.element.content=document.createElement("div"),e.element.content.contentEditable=!0,e.element.content.className=e.classes.content,e.element.content.oninput=function(r){return e.onChange(r.target.innerHTML)},e.element.content.onkeydown=o,e.element.appendChild(e.element.content),e.actions.forEach(function(r){var t=document.createElement("button");t.className=e.classes.button,t.innerHTML=r.icon,t.title=r.title,t.onclick=r.result,i.appendChild(t)}),e.styleWithCSS&&a("styleWithCSS"),e.element},s={exec:a,init:i};e.exec=a,e.init=i,e.default=s,Object.defineProperty(e,"__esModule",{value:!0})})}));!function(e,r){if("undefined"==typeof document)return r;e=e||"";var t=document.head||document.getElementsByTagName("head")[0],n=document.createElement("style");n.type="text/css",t.appendChild(n),n.styleSheet?n.styleSheet.cssText=e:n.appendChild(document.createTextNode(e))}(".pell{border-radius:5px;box-shadow:0 2px 3px hsla(0,0%,4%,.1),0 0 0 1px hsla(0,0%,4%,.1)}.pell,.pell-content{box-sizing:border-box;width:100%}.pell-content{height:300px;outline:0;overflow-y:auto;padding:10px}.pell-actionbar{background-color:#fff;border-bottom:1px solid hsla(0,0%,4%,.1);border-top-left-radius:5px;border-top-right-radius:5px;width:100%}.pell-button{background-color:transparent;border:none;cursor:pointer;height:30px;outline:0;width:30px}",void 0);var s="function"==typeof Symbol&&"symbol"==typeof Symbol.iterator?function(e){return typeof e}:function(e){return e&&"function"==typeof Symbol&&e.constructor===Symbol&&e!==Symbol.prototype?"symbol":typeof e},l=function(e,r){if(!(e instanceof r))throw new TypeError("Cannot call a class as a function")},c=function(){function e(e,r){for(var t=0;t (GFM Style)",type:"boolean"},requireSpaceBeforeHeadingText:{defaultValue:!1,description:"Makes adding a space between `#` and the header text mandatory (GFM Style)",type:"boolean"},ghMentions:{defaultValue:!1,description:"Enables github @mentions",type:"boolean"},ghMentionsLink:{defaultValue:"https://github.com/{u}",description:"Changes the link generated by @mentions. Only applies if ghMentions option is enabled.",type:"string"},encodeEmails:{defaultValue:!0,description:"Encode e-mail addresses through the use of Character Entities, transforming ASCII e-mail addresses into its equivalent decimal entities",type:"boolean"},openLinksInNewWindow:{defaultValue:!1,description:"Open all links in new windows",type:"boolean"}};if(!1===e)return JSON.parse(JSON.stringify(r));var t={};for(var n in r)r.hasOwnProperty(n)&&(t[n]=r[n].defaultValue);return t}function t(e,r){var t=r?"Error in "+r+" extension->":"Error in unnamed extension",n={valid:!0,error:""};a.helper.isArray(e)||(e=[e]);for(var o=0;o-1,p=new RegExp(r+"|"+t,"g"+c.replace(/g/g,"")),d=new RegExp(r,c.replace(/g/g,"")),f=[];do{for(a=0;i=p.exec(e);)if(d.test(i[0]))a++||(s=(o=p.lastIndex)-i[0].length);else if(a&&!--a){l=i.index+i[0].length;var h={left:{start:s,end:o},match:{start:o,end:i.index},right:{start:i.index,end:l},wholeMatch:{start:s,end:l}};if(f.push(h),!u)return f}}while(a&&(p.lastIndex=o));return f};a.helper.matchRecursiveRegExp=function(e,r,t,n){for(var a=u(e,r,t,n),o=[],i=0;i0){var p=[];0!==s[0].wholeMatch.start&&p.push(e.slice(0,s[0].wholeMatch.start));for(var d=0;d=0?n+(t||0):n},a.helper.splitAtIndex=function(e,r){if(!a.helper.isString(e))throw"InvalidArgumentError: first parameter of showdown.helper.regexIndexOf function must be a string";return[e.substring(0,r),e.substring(r)]},a.helper.encodeEmailAddress=function(e){var r=[function(e){return"&#"+e.charCodeAt(0)+";"},function(e){return"&#x"+e.charCodeAt(0).toString(16)+";"},function(e){return e}];return e=e.replace(/./g,function(e){if("@"===e)e=r[Math.floor(2*Math.random())](e);else{var t=Math.random();e=t>.9?r[2](e):t>.45?r[1](e):r[0](e)}return e})},"undefined"==typeof console&&(console={warn:function(e){alert(e)},log:function(e){alert(e)},error:function(e){throw e}}),a.helper.regexes={asteriskAndDash:/([*_])/g},a.Converter=function(e){function r(e,r){if(r=r||null,a.helper.isString(e)){if(e=a.helper.stdExtName(e),r=e,a.extensions[e])return console.warn("DEPRECATION WARNING: "+e+" is an old extension that uses a deprecated loading method.Please inform the developer that the extension should be updated!"),void n(a.extensions[e],e);if(a.helper.isUndefined(i[e]))throw Error('Extension "'+e+'" could not be loaded. It was either not found or is not a valid extension.');e=i[e]}"function"==typeof e&&(e=e()),a.helper.isArray(e)||(e=[e]);var s=t(e,r);if(!s.valid)throw Error(s.error);for(var l=0;l? ?(['"].*['"])?\)$/m)>-1)i="";else if(!i){if(o||(o=n.toLowerCase().replace(/ ?\n/g," ")),i="#"+o,a.helper.isUndefined(t.gUrls[o]))return e;i=t.gUrls[o],a.helper.isUndefined(t.gTitles[o])||(c=t.gTitles[o])}var u='"};return e=(e=t.converter._dispatch("anchors.before",e,r,t)).replace(/\[((?:\[[^\]]*]|[^\[\]])*)] ?(?:\n *)?\[(.*?)]()()()()/g,n),e=e.replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]?<([^>]*)>(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,n),e=e.replace(/\[((?:\[[^\]]*]|[^\[\]])*)]()[ \t]*\([ \t]??(?:[ \t]*((["'])([^"]*?)\5))?[ \t]?\)/g,n),e=e.replace(/\[([^\[\]]+)]()()()()()/g,n),r.ghMentions&&(e=e.replace(/(^|\s)(\\)?(@([a-z\d\-]+))(?=[.!?;,[\]()]|\s|$)/gim,function(e,t,n,o,i){if("\\"===n)return t+o;if(!a.helper.isString(r.ghMentionsLink))throw new Error("ghMentionsLink option must be a string");return t+''+o+""})),e=t.converter._dispatch("anchors.after",e,r,t)});var p=/\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+)()(?=\s|$)(?!["<>])/gi,d=/\b(((https?|ftp|dict):\/\/|www\.)[^'">\s]+\.[^'">\s]+?)([.!?,()\[\]]?)(?=\s|$)(?!["<>])/gi,f=/<(((https?|ftp|dict):\/\/|www\.)[^'">\s]+)()>/gi,h=/(^|\s)(?:mailto:)?([A-Za-z0-9!#$%&'*+-/=?^_`{|}~.]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)(?=$|\s)/gim,g=/<()(?:mailto:)?([-.\w]+@[-a-z0-9]+(\.[-a-z0-9]+)*\.[a-z]+)>/gi,b=function(e){return function(r,t,n,a,o){var i=t,s="",l="";return/^www\./i.test(t)&&(t=t.replace(/^www\./i,"http://www.")),e.excludeTrailingPunctuationFromURLs&&o&&(s=o),e.openLinksInNewWindow&&(l=' target="¨E95Eblank"'),'"+i+""+s}},m=function(e,r){return function(t,n,o){var i="mailto:";return n=n||"",o=a.subParser("unescapeSpecialChars")(o,e,r),e.encodeEmails?(i=a.helper.encodeEmailAddress(i+o),o=a.helper.encodeEmailAddress(o)):i+=o,n+''+o+""}};a.subParser("autoLinks",function(e,r,t){return e=t.converter._dispatch("autoLinks.before",e,r,t),e=e.replace(f,b(r)),e=e.replace(g,m(r,t)),e=t.converter._dispatch("autoLinks.after",e,r,t)}),a.subParser("simplifiedAutoLinks",function(e,r,t){return r.simplifiedAutoLink?(e=t.converter._dispatch("simplifiedAutoLinks.before",e,r,t),e=r.excludeTrailingPunctuationFromURLs?e.replace(d,b(r)):e.replace(p,b(r)),e=e.replace(h,m(r,t)),e=t.converter._dispatch("simplifiedAutoLinks.after",e,r,t)):e}),a.subParser("blockGamut",function(e,r,t){return e=t.converter._dispatch("blockGamut.before",e,r,t),e=a.subParser("blockQuotes")(e,r,t),e=a.subParser("headers")(e,r,t),e=a.subParser("horizontalRule")(e,r,t),e=a.subParser("lists")(e,r,t),e=a.subParser("codeBlocks")(e,r,t),e=a.subParser("tables")(e,r,t),e=a.subParser("hashHTMLBlocks")(e,r,t),e=a.subParser("paragraphs")(e,r,t),e=t.converter._dispatch("blockGamut.after",e,r,t)}),a.subParser("blockQuotes",function(e,r,t){return e=t.converter._dispatch("blockQuotes.before",e,r,t),e=e.replace(/((^ {0,3}>[ \t]?.+\n(.+\n)*\n*)+)/gm,function(e,n){var o=n;return o=o.replace(/^[ \t]*>[ \t]?/gm,"¨0"),o=o.replace(/¨0/g,""),o=o.replace(/^[ \t]+$/gm,""),o=a.subParser("githubCodeBlocks")(o,r,t),o=a.subParser("blockGamut")(o,r,t),o=o.replace(/(^|\n)/g,"$1  "),o=o.replace(/(\s*
[^\r]+?<\/pre>)/gm,function(e,r){var t=r;return t=t.replace(/^  /gm,"¨0"),t=t.replace(/¨0/g,"")}),a.subParser("hashBlock")("
\n"+o+"\n
",r,t)}),e=t.converter._dispatch("blockQuotes.after",e,r,t)}),a.subParser("codeBlocks",function(e,r,t){e=t.converter._dispatch("codeBlocks.before",e,r,t);var n=/(?:\n\n|^)((?:(?:[ ]{4}|\t).*\n+)+)(\n*[ ]{0,3}[^ \t\n]|(?=¨0))/g;return e=(e+="¨0").replace(n,function(e,n,o){var i=n,s=o,l="\n";return i=a.subParser("outdent")(i,r,t),i=a.subParser("encodeCode")(i,r,t),i=a.subParser("detab")(i,r,t),i=i.replace(/^\n+/g,""),i=i.replace(/\n+$/g,""),r.omitExtraWLInCodeBlocks&&(l=""),i="
"+i+l+"
",a.subParser("hashBlock")(i,r,t)+s}),e=e.replace(/¨0/,""),e=t.converter._dispatch("codeBlocks.after",e,r,t)}),a.subParser("codeSpans",function(e,r,t){return void 0===(e=t.converter._dispatch("codeSpans.before",e,r,t))&&(e=""),e=e.replace(/(^|[^\\])(`+)([^\r]*?[^`])\2(?!`)/gm,function(e,n,o,i){var s=i;return s=s.replace(/^([ \t]*)/g,""),s=s.replace(/[ \t]*$/g,""),s=a.subParser("encodeCode")(s,r,t),n+""+s+""}),e=t.converter._dispatch("codeSpans.after",e,r,t)}),a.subParser("detab",function(e,r,t){return e=t.converter._dispatch("detab.before",e,r,t),e=e.replace(/\t(?=\t)/g," "),e=e.replace(/\t/g,"¨A¨B"),e=e.replace(/¨B(.+?)¨A/g,function(e,r){for(var t=r,n=4-t.length%4,a=0;a/g,">"),e=t.converter._dispatch("encodeAmpsAndAngles.after",e,r,t)}),a.subParser("encodeBackslashEscapes",function(e,r,t){return e=t.converter._dispatch("encodeBackslashEscapes.before",e,r,t),e=e.replace(/\\(\\)/g,a.helper.escapeCharactersCallback),e=e.replace(/\\([`*_{}\[\]()>#+.!~=|-])/g,a.helper.escapeCharactersCallback),e=t.converter._dispatch("encodeBackslashEscapes.after",e,r,t)}),a.subParser("encodeCode",function(e,r,t){return e=t.converter._dispatch("encodeCode.before",e,r,t),e=e.replace(/&/g,"&").replace(//g,">").replace(/([*_{}\[\]\\=~-])/g,a.helper.escapeCharactersCallback),e=t.converter._dispatch("encodeCode.after",e,r,t)}),a.subParser("escapeSpecialCharsWithinTagAttributes",function(e,r,t){var n=/(<[a-z\/!$]("[^"]*"|'[^']*'|[^'">])*>|)/gi;return e=(e=t.converter._dispatch("escapeSpecialCharsWithinTagAttributes.before",e,r,t)).replace(n,function(e){return e.replace(/(.)<\/?code>(?=.)/g,"$1`").replace(/([\\`*_~=|])/g,a.helper.escapeCharactersCallback)}),e=t.converter._dispatch("escapeSpecialCharsWithinTagAttributes.after",e,r,t)}),a.subParser("githubCodeBlocks",function(e,r,t){return r.ghCodeBlocks?(e=t.converter._dispatch("githubCodeBlocks.before",e,r,t),e+="¨0",e=e.replace(/(?:^|\n)```(.*)\n([\s\S]*?)\n```/g,function(e,n,o){var i=r.omitExtraWLInCodeBlocks?"":"\n";return o=a.subParser("encodeCode")(o,r,t),o=a.subParser("detab")(o,r,t),o=o.replace(/^\n+/g,""),o=o.replace(/\n+$/g,""),o="
"+o+i+"
",o=a.subParser("hashBlock")(o,r,t),"\n\n¨G"+(t.ghCodeBlocks.push({text:e,codeblock:o})-1)+"G\n\n"}),e=e.replace(/¨0/,""),t.converter._dispatch("githubCodeBlocks.after",e,r,t)):e}),a.subParser("hashBlock",function(e,r,t){return e=t.converter._dispatch("hashBlock.before",e,r,t),e=e.replace(/(^\n+|\n+$)/g,""),e="\n\n¨K"+(t.gHtmlBlocks.push(e)-1)+"K\n\n",e=t.converter._dispatch("hashBlock.after",e,r,t)}),a.subParser("hashCodeTags",function(e,r,t){e=t.converter._dispatch("hashCodeTags.before",e,r,t);return e=a.helper.replaceRecursiveRegExp(e,function(e,n,o,i){var s=o+a.subParser("encodeCode")(n,r,t)+i;return"¨C"+(t.gHtmlSpans.push(s)-1)+"C"},"]*>","
","gim"),e=t.converter._dispatch("hashCodeTags.after",e,r,t)}),a.subParser("hashElement",function(e,r,t){return function(e,r){var n=r;return n=n.replace(/\n\n/g,"\n"),n=n.replace(/^\n/,""),n=n.replace(/\n+$/g,""),n="\n\n¨K"+(t.gHtmlBlocks.push(n)-1)+"K\n\n"}}),a.subParser("hashHTMLBlocks",function(e,r,t){e=t.converter._dispatch("hashHTMLBlocks.before",e,r,t);for(var n=["pre","div","h1","h2","h3","h4","h5","h6","blockquote","table","dl","ol","ul","script","noscript","form","fieldset","iframe","math","style","section","header","footer","nav","article","aside","address","audio","canvas","figure","hgroup","output","video","p"],o=0;o]*>","im"),l="<"+n[o]+"\\b[^>]*>",c="";-1!==(i=a.helper.regexIndexOf(e,s));){var u=a.helper.splitAtIndex(e,i),p=a.helper.replaceRecursiveRegExp(u[1],function(e,r,n,a){var o=e;return-1!==n.search(/\bmarkdown\b/)&&(o=n+t.converter.makeHtml(r)+a),"\n\n¨K"+(t.gHtmlBlocks.push(o)-1)+"K\n\n"},l,c,"im");if(p===u[1])break;e=u[0].concat(p)}return e=e.replace(/(\n {0,3}(<(hr)\b([^<>])*?\/?>)[ \t]*(?=\n{2,}))/g,a.subParser("hashElement")(e,r,t)),e=a.helper.replaceRecursiveRegExp(e,function(e){return"\n\n¨K"+(t.gHtmlBlocks.push(e)-1)+"K\n\n"},"^ {0,3}\x3c!--","--\x3e","gm"),e=e.replace(/(?:\n\n)( {0,3}(?:<([?%])[^\r]*?\2>)[ \t]*(?=\n{2,}))/g,a.subParser("hashElement")(e,r,t)),e=t.converter._dispatch("hashHTMLBlocks.after",e,r,t)}),a.subParser("hashHTMLSpans",function(e,r,t){function n(e){return"¨C"+(t.gHtmlSpans.push(e)-1)+"C"}return e=t.converter._dispatch("hashHTMLSpans.before",e,r,t),e=e.replace(/<[^>]+?\/>/gi,function(e){return n(e)}),e=e.replace(/<([^>]+?)>[\s\S]*?<\/\1>/g,function(e){return n(e)}),e=e.replace(/<([^>]+?)\s[^>]+?>[\s\S]*?<\/\1>/g,function(e){return n(e)}),e=e.replace(/<[^>]+?>/gi,function(e){return n(e)}),e=t.converter._dispatch("hashHTMLSpans.after",e,r,t)}),a.subParser("unhashHTMLSpans",function(e,r,t){e=t.converter._dispatch("unhashHTMLSpans.before",e,r,t);for(var n=0;n]*>\\s*]*>","^ {0,3}\\s*
","gim"),e=t.converter._dispatch("hashPreCodeTags.after",e,r,t)}),a.subParser("headers",function(e,r,t){function n(e){var n;if(r.customizedHeaderId){var o=e.match(/\{([^{]+?)}\s*$/);o&&o[1]&&(e=o[1])}return n=a.helper.isString(r.prefixHeaderId)?r.prefixHeaderId+e:!0===r.prefixHeaderId?"section "+e:e,n=i?n.replace(/ /g,"-").replace(/&/g,"").replace(/¨T/g,"").replace(/¨D/g,"").replace(/[&+$,\/:;=?@"#{}|^¨~\[\]`\\*)(%.!'<>]/g,"").toLowerCase():n.replace(/[^\w]/g,"").toLowerCase(),t.hashLinkCounts[n]?n=n+"-"+t.hashLinkCounts[n]++:t.hashLinkCounts[n]=1,n}e=t.converter._dispatch("headers.before",e,r,t);var o=isNaN(parseInt(r.headerLevelStart))?1:parseInt(r.headerLevelStart),i=r.ghCompatibleHeaderId,s=r.smoothLivePreview?/^(.+)[ \t]*\n={2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n=+[ \t]*\n+/gm,l=r.smoothLivePreview?/^(.+)[ \t]*\n-{2,}[ \t]*\n+/gm:/^(.+)[ \t]*\n-+[ \t]*\n+/gm;e=(e=e.replace(s,function(e,i){var s=a.subParser("spanGamut")(i,r,t),l=r.noHeaderId?"":' id="'+n(i)+'"',c=o,u=""+s+"";return a.subParser("hashBlock")(u,r,t)})).replace(l,function(e,i){var s=a.subParser("spanGamut")(i,r,t),l=r.noHeaderId?"":' id="'+n(i)+'"',c=o+1,u=""+s+"";return a.subParser("hashBlock")(u,r,t)});var c=r.requireSpaceBeforeHeadingText?/^(#{1,6})[ \t]+(.+?)[ \t]*#*\n+/gm:/^(#{1,6})[ \t]*(.+?)[ \t]*#*\n+/gm;return e=e.replace(c,function(e,i,s){var l=s;r.customizedHeaderId&&(l=s.replace(/\s?\{([^{]+?)}\s*$/,""));var c=a.subParser("spanGamut")(l,r,t),u=r.noHeaderId?"":' id="'+n(s)+'"',p=o-1+i.length,d=""+c+"";return a.subParser("hashBlock")(d,r,t)}),e=t.converter._dispatch("headers.after",e,r,t)}),a.subParser("horizontalRule",function(e,r,t){e=t.converter._dispatch("horizontalRule.before",e,r,t);var n=a.subParser("hashBlock")("
",r,t);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=t.converter._dispatch("horizontalRule.after",e,r,t)}),a.subParser("images",function(e,r,t){function n(e,r,n,o,i,s,l,c){var u=t.gUrls,p=t.gTitles,d=t.gDimensions;if(n=n.toLowerCase(),c||(c=""),e.search(/\(? ?(['"].*['"])?\)$/m)>-1)o="";else if(""===o||null===o){if(""!==n&&null!==n||(n=r.toLowerCase().replace(/ ?\n/g," ")),o="#"+n,a.helper.isUndefined(u[n]))return e;o=u[n],a.helper.isUndefined(p[n])||(c=p[n]),a.helper.isUndefined(d[n])||(i=d[n].width,s=d[n].height)}r=r.replace(/"/g,""").replace(a.helper.regexes.asteriskAndDash,a.helper.escapeCharactersCallback);var f=''+r+'"}var o=/!\[([^\]]*?)][ \t]*()\([ \t]??(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(["'])([^"]*?)\6)?[ \t]?\)/g,i=/!\[([^\]]*?)][ \t]*()\([ \t]?<([^>]*)>(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*(?:(?:(["'])([^"]*?)\6))?[ \t]?\)/g,s=/!\[([^\]]*?)] ?(?:\n *)?\[(.*?)]()()()()()/g,l=/!\[([^\[\]]+)]()()()()()/g;return e=(e=t.converter._dispatch("images.before",e,r,t)).replace(s,n),e=e.replace(i,n),e=e.replace(o,n),e=e.replace(l,n),e=t.converter._dispatch("images.after",e,r,t)}),a.subParser("italicsAndBold",function(e,r,t){function n(e,n,o){return r.simplifiedAutoLink&&(e=a.subParser("simplifiedAutoLinks")(e,r,t)),n+e+o}return e=t.converter._dispatch("italicsAndBold.before",e,r,t),e=r.literalMidWordUnderscores?(e=(e=e.replace(/\b___(\S[\s\S]*)___\b/g,function(e,r){return n(r,"","")})).replace(/\b__(\S[\s\S]*)__\b/g,function(e,r){return n(r,"","")})).replace(/\b_(\S[\s\S]*?)_\b/g,function(e,r){return n(r,"","")}):(e=(e=e.replace(/___(\S[\s\S]*?)___/g,function(e,r){return/\S$/.test(r)?n(r,"",""):e})).replace(/__(\S[\s\S]*?)__/g,function(e,r){return/\S$/.test(r)?n(r,"",""):e})).replace(/_([^\s_][\s\S]*?)_/g,function(e,r){return/\S$/.test(r)?n(r,"",""):e}),e=r.literalMidWordAsterisks?(e=(e=e.trim().replace(/(?:^| +)\*{3}(\S[\s\S]*?)\*{3}(?: +|$)/g,function(e,r){return n(r," "," ")})).trim().replace(/(?:^| +)\*{2}(\S[\s\S]*?)\*{2}(?: +|$)/g,function(e,r){return n(r," "," ")})).trim().replace(/(?:^| +)\*{1}(\S[\s\S]*?)\*{1}(?: +|$)/g,function(e,r){return n(r," ",""+(" "===e.slice(-1)?" ":""))}):(e=(e=e.replace(/\*\*\*(\S[\s\S]*?)\*\*\*/g,function(e,r){return/\S$/.test(r)?n(r,"",""):e})).replace(/\*\*(\S[\s\S]*?)\*\*/g,function(e,r){return/\S$/.test(r)?n(r,"",""):e})).replace(/\*([^\s*][\s\S]*?)\*/g,function(e,r){return/\S$/.test(r)?n(r,"",""):e}),e=t.converter._dispatch("italicsAndBold.after",e,r,t)}),a.subParser("lists",function(e,r,t){function n(e,n){t.gListLevel++,e=e.replace(/\n{2,}$/,"\n"),e+="¨0";var o=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0| {0,3}([*+-]|\d+[.])[ \t]+))/gm,i=/\n[ \t]*\n(?!¨0)/.test(e);return r.disableForced4SpacesIndentedSublists&&(o=/(\n)?(^ {0,3})([*+-]|\d+[.])[ \t]+((\[(x|X| )?])?[ \t]*[^\r]+?(\n{1,2}))(?=\n*(¨0|\2([*+-]|\d+[.])[ \t]+))/gm),e=e.replace(o,function(e,n,o,s,l,c,u){u=u&&""!==u.trim();var p=a.subParser("outdent")(l,r,t),d="";return c&&r.tasklists&&(d=' class="task-list-item" style="list-style-type: none;"',p=p.replace(/^[ \t]*\[(x|X| )?]/m,function(){var e='-1?(p=a.subParser("githubCodeBlocks")(p,r,t),p=a.subParser("blockGamut")(p,r,t)):(p=(p=a.subParser("lists")(p,r,t)).replace(/\n$/,""),p=(p=(p=a.subParser("hashHTMLBlocks")(p,r,t)).replace(/\n\n+/g,"\n\n")).replace(/\n\n/g,"¨B"),p=(p=i?a.subParser("paragraphs")(p,r,t):a.subParser("spanGamut")(p,r,t)).replace(/¨B/g,"\n\n")),p=p.replace("¨A",""),p=""+p+"\n"}),e=e.replace(/¨0/g,""),t.gListLevel--,n&&(e=e.replace(/\s+$/,"")),e}function o(e,t,a){var o=r.disableForced4SpacesIndentedSublists?/^ ?\d+\.[ \t]/gm:/^ {0,3}\d+\.[ \t]/gm,i=r.disableForced4SpacesIndentedSublists?/^ ?[*+-][ \t]/gm:/^ {0,3}[*+-][ \t]/gm,s="ul"===t?o:i,l="";return-1!==e.search(s)?function e(r){var c=r.search(s);-1!==c?(l+="\n<"+t+">\n"+n(r.slice(0,c),!!a)+"\n",s="ul"===(t="ul"===t?"ol":"ul")?o:i,e(r.slice(c))):l+="\n<"+t+">\n"+n(r,!!a)+"\n"}(e):l="\n<"+t+">\n"+n(e,!!a)+"\n",l}return e=t.converter._dispatch("lists.before",e,r,t),e+="¨0",e=t.gListLevel?e.replace(/^(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,function(e,r,t){return o(r,t.search(/[*+-]/g)>-1?"ul":"ol",!0)}):e.replace(/(\n\n|^\n?)(( {0,3}([*+-]|\d+[.])[ \t]+)[^\r]+?(¨0|\n{2,}(?=\S)(?![ \t]*(?:[*+-]|\d+[.])[ \t]+)))/gm,function(e,r,t,n){return o(t,n.search(/[*+-]/g)>-1?"ul":"ol",!1)}),e=e.replace(/¨0/,""),e=t.converter._dispatch("lists.after",e,r,t)}),a.subParser("outdent",function(e,r,t){return e=t.converter._dispatch("outdent.before",e,r,t),e=e.replace(/^(\t|[ ]{1,4})/gm,"¨0"),e=e.replace(/¨0/g,""),e=t.converter._dispatch("outdent.after",e,r,t)}),a.subParser("paragraphs",function(e,r,t){for(var n=(e=(e=(e=t.converter._dispatch("paragraphs.before",e,r,t)).replace(/^\n+/g,"")).replace(/\n+$/g,"")).split(/\n{2,}/g),o=[],i=n.length,s=0;s=0?o.push(l):l.search(/\S/)>=0&&(l=(l=a.subParser("spanGamut")(l,r,t)).replace(/^([ \t]*)/g,"

"),l+="

",o.push(l))}for(i=o.length,s=0;s]*>\s*]*>/.test(u)&&(p=!0)}o[s]=u}return e=o.join("\n"),e=e.replace(/^\n+/g,""),e=e.replace(/\n+$/g,""),t.converter._dispatch("paragraphs.after",e,r,t)}),a.subParser("runExtension",function(e,r,t,n){if(e.filter)r=e.filter(r,n.converter,t);else if(e.regex){var a=e.regex;a instanceof RegExp||(a=new RegExp(a,"g")),r=r.replace(a,e.replace)}return r}),a.subParser("spanGamut",function(e,r,t){return e=t.converter._dispatch("spanGamut.before",e,r,t),e=a.subParser("codeSpans")(e,r,t),e=a.subParser("escapeSpecialCharsWithinTagAttributes")(e,r,t),e=a.subParser("encodeBackslashEscapes")(e,r,t),e=a.subParser("images")(e,r,t),e=a.subParser("anchors")(e,r,t),e=a.subParser("autoLinks")(e,r,t),e=a.subParser("italicsAndBold")(e,r,t),e=a.subParser("strikethrough")(e,r,t),e=a.subParser("simplifiedAutoLinks")(e,r,t),e=a.subParser("hashHTMLSpans")(e,r,t),e=a.subParser("encodeAmpsAndAngles")(e,r,t),e=r.simpleLineBreaks?e.replace(/\n/g,"
\n"):e.replace(/ +\n/g,"
\n"),e=t.converter._dispatch("spanGamut.after",e,r,t)}),a.subParser("strikethrough",function(e,r,t){function n(e){return r.simplifiedAutoLink&&(e=a.subParser("simplifiedAutoLinks")(e,r,t)),""+e+""}return r.strikethrough&&(e=(e=t.converter._dispatch("strikethrough.before",e,r,t)).replace(/(?:~){2}([\s\S]+?)(?:~){2}/g,function(e,r){return n(r)}),e=t.converter._dispatch("strikethrough.after",e,r,t)),e}),a.subParser("stripLinkDefinitions",function(e,r,t){var n=/^ {0,3}\[(.+)]:[ \t]*\n?[ \t]*\s]+)>?(?: =([*\d]+[A-Za-z%]{0,4})x([*\d]+[A-Za-z%]{0,4}))?[ \t]*\n?[ \t]*(?:(\n*)["|'(](.+?)["|')][ \t]*)?(?:\n+|(?=¨0))/gm;return e+="¨0",e=e.replace(n,function(e,n,o,i,s,l,c){return n=n.toLowerCase(),t.gUrls[n]=a.subParser("encodeAmpsAndAngles")(o,r,t),l?l+c:(c&&(t.gTitles[n]=c.replace(/"|'/g,""")),r.parseImgDimensions&&i&&s&&(t.gDimensions[n]={width:i,height:s}),"")}),e=e.replace(/¨0/,"")}),a.subParser("tables",function(e,r,t){function n(e){return/^:[ \t]*--*$/.test(e)?' style="text-align:left;"':/^--*[ \t]*:[ \t]*$/.test(e)?' style="text-align:right;"':/^:[ \t]*--*[ \t]*:$/.test(e)?' style="text-align:center;"':""}function o(e,n){var o="";return e=e.trim(),r.tableHeaderId&&(o=' id="'+e.replace(/ /g,"_").toLowerCase()+'"'),e=a.subParser("spanGamut")(e,r,t),""+e+"\n"}function i(e,n){return""+a.subParser("spanGamut")(e,r,t)+"\n"}function s(e,r){for(var t="\n\n\n",n=e.length,a=0;a\n\n\n",a=0;a\n";for(var o=0;o\n"}return t+="\n
\n"}if(!r.tables)return e;var l=/^ {0,3}\|?.+\|.+\n[ \t]{0,3}\|?[ \t]*:?[ \t]*(?:-|=){2,}[ \t]*:?[ \t]*\|[ \t]*:?[ \t]*(?:-|=){2,}[\s\S]+?(?:\n\n|¨0)/gm;return e=t.converter._dispatch("tables.before",e,r,t),e=e.replace(/\\(\|)/g,a.helper.escapeCharactersCallback),e=e.replace(l,function(e){var r,t=e.split("\n");for(r=0;r0;)for(r=a.shift(),o.push(r),t=r.childNodes,n=0;n=0;a--)p(n[a]);return s(t).replace(/^[\t\r\n]+|[\t\r\n\s]+$/g,"").replace(/\n\s+\n/g,"\n\n").replace(/\n{3,}/g,"\n\n")}).isBlock=n,d.isVoid=a,d.outer=function(e,r){return e.cloneNode(!1).outerHTML.replace("><",">"+r+"<")},r.exports=d},{"./lib/gfm-converters":2,"./lib/html-parser":3,"./lib/md-converters":4,"collapse-whitespace":7}],2:[function(e,r,t){function n(e,r){var t=" ";return 0===Array.prototype.indexOf.call(r.parentNode.childNodes,r)&&(t="| "),t+e+" |"}var a=/highlight highlight-(\S+)/;r.exports=[{filter:"br",replacement:function(){return"\n"}},{filter:["del","s","strike"],replacement:function(e){return"~~"+e+"~~"}},{filter:function(e){return"checkbox"===e.type&&"LI"===e.parentNode.nodeName},replacement:function(e,r){return(r.checked?"[x]":"[ ]")+" "}},{filter:["th","td"],replacement:function(e,r){return n(e,r)}},{filter:"tr",replacement:function(e,r){var t="",a={left:":--",right:"--:",center:":-:"};if("THEAD"===r.parentNode.nodeName)for(var o=0;o "))+"\n\n"}},{filter:"li",replacement:function(e,r){e=e.replace(/^\s+/,"").replace(/\n/gm,"\n ");var t=r.parentNode,n=Array.prototype.indexOf.call(t.children,r)+1;return(/ol/i.test(t.nodeName)?n+". ":"* ")+e}},{filter:["ul","ol"],replacement:function(e,r){for(var t=[],n=0;n