diff --git a/package.json b/package.json
index 30d972c6..c91c9083 100644
--- a/package.json
+++ b/package.json
@@ -76,6 +76,7 @@
"lodash": "^4.13.1",
"markup-it": "git+https://github.com/cassiozen/markup-it.git",
"pluralize": "^3.0.0",
+ "prismjs": "^1.5.1",
"react-portal": "^2.2.1",
"selection-position": "^1.0.0",
"slate": "^0.12.2"
diff --git a/src/components/Widgets/MarkdownControlElements/RawEditor/index.css b/src/components/Widgets/MarkdownControlElements/RawEditor/index.css
new file mode 100644
index 00000000..e69de29b
diff --git a/src/components/Widgets/MarkdownControlElements/RawEditor/index.js b/src/components/Widgets/MarkdownControlElements/RawEditor/index.js
new file mode 100644
index 00000000..2cabeaa0
--- /dev/null
+++ b/src/components/Widgets/MarkdownControlElements/RawEditor/index.js
@@ -0,0 +1,122 @@
+import React, { PropTypes } from 'react';
+import { Editor, Plain, Mark } from 'slate';
+import Prism from 'prismjs';
+import marks from './prismMarkdown';
+import styles from './index.css';
+
+const MARKS = {
+ 'highlight-comment': {
+ opacity: '0.33'
+ },
+ 'highlight-important': {
+ fontWeight: 'bold',
+ color: '#006',
+ },
+ 'highlight-keyword': {
+ fontWeight: 'bold',
+ color: '#006',
+ },
+ 'highlight-url': {
+ color: '#006',
+ },
+ 'highlight-punctuation': {
+ color: '#006',
+ }
+};
+
+Prism.languages.markdown = Prism.languages.extend('markup', {});
+Prism.languages.insertBefore('markdown', 'prolog', marks);
+Prism.languages.markdown['bold'].inside['url'] = Prism.util.clone(Prism.languages.markdown['url']);
+Prism.languages.markdown['italic'].inside['url'] = Prism.util.clone(Prism.languages.markdown['url']);
+Prism.languages.markdown['bold'].inside['italic'] = Prism.util.clone(Prism.languages.markdown['italic']);
+Prism.languages.markdown['italic'].inside['bold'] = Prism.util.clone(Prism.languages.markdown['bold']);
+
+class RawEditor extends React.Component {
+
+ constructor(props) {
+ super(props);
+
+ const content = props.value ? Plain.deserialize(props.value) : Plain.deserialize('');
+
+ this.state = {
+ state: content
+ };
+
+ this.handleChange = this.handleChange.bind(this);
+ this.handleDocumentChange = this.handleDocumentChange.bind(this);
+ this.renderMark = this.renderMark.bind(this);
+ this.renderDecorations = this.renderDecorations.bind(this);
+
+ }
+
+ /**
+ * Slate keeps track of selections, scroll position etc.
+ * So, onChange gets dispatched on every interaction (click, arrows, everything...)
+ * It also have an onDocumentChange, that get's dispached only when the actual
+ * content changes
+ */
+ handleChange(state) {
+ this.setState({ state });
+ }
+
+ handleDocumentChange(document, state) {
+ const content = Plain.serialize(state, { terse: true });
+ this.props.onChange(content);
+ }
+
+ renderMark(mark) {
+ return MARKS[mark.type] || {};
+ }
+
+ renderDecorations(text, block) {
+ let characters = text.characters.asMutable();
+ const string = text.text;
+ const grammar = Prism.languages.markdown;
+ const tokens = Prism.tokenize(string, grammar);
+ let offset = 0;
+
+ for (const token of tokens) {
+ if (typeof token == 'string') {
+ offset += token.length;
+ continue;
+ }
+
+ const length = offset + token.matchedStr.length;
+ const name = token.alias || token.type;
+ const type = `highlight-${name}`;
+
+ for (let i = offset; i < length; i++) {
+ let char = characters.get(i);
+ let { marks } = char;
+ marks = marks.add(Mark.create({ type }));
+ char = char.merge({ marks });
+ characters = characters.set(i, char);
+ }
+
+ offset = length;
+ }
+
+ return characters.asImmutable();
+ }
+
+
+ render() {
+ return (
+
+ );
+ }
+}
+
+export default RawEditor;
+
+RawEditor.propTypes = {
+ onChange: PropTypes.func.isRequired,
+ value: PropTypes.node,
+};
diff --git a/src/components/Widgets/MarkdownControlElements/RawEditor/prismMarkdown.js b/src/components/Widgets/MarkdownControlElements/RawEditor/prismMarkdown.js
new file mode 100644
index 00000000..1ea5e5d3
--- /dev/null
+++ b/src/components/Widgets/MarkdownControlElements/RawEditor/prismMarkdown.js
@@ -0,0 +1,116 @@
+const marks = {
+ 'blockquote': {
+ // > ...
+ pattern: /^>(?:[\t ]*>)*/m,
+ alias: 'punctuation'
+ },
+ 'code': [
+ {
+ // Prefixed by 4 spaces or 1 tab
+ pattern: /^(?: {4}|\t).+/m,
+ alias: 'keyword'
+ },
+ {
+ // `code`
+ // ``code``
+ pattern: /``.+?``|`[^`\n]+`/,
+ alias: 'keyword'
+ }
+ ],
+ 'title': [
+ {
+ // title 1
+ // =======
+
+ // title 2
+ // -------
+ pattern: /\w+.*(?:\r?\n|\r)(?:==+|--+)/,
+ alias: 'important',
+ inside: {
+ punctuation: /==+$|--+$/
+ }
+ },
+ {
+ // # title 1
+ // ###### title 6
+ pattern: /(^\s*)#+.+/m,
+ lookbehind: true,
+ alias: 'important',
+ inside: {
+ punctuation: /^#+|#+$/
+ }
+ }
+ ],
+ 'hr': {
+ // ***
+ // ---
+ // * * *
+ // -----------
+ pattern: /(^\s*)([*-])([\t ]*\2){2,}(?=\s*$)/m,
+ lookbehind: true,
+ alias: 'punctuation'
+ },
+ 'list': {
+ // * item
+ // + item
+ // - item
+ // 1. item
+ pattern: /(^\s*)(?:[*+-]|\d+\.)(?=[\t ].)/m,
+ lookbehind: true,
+ alias: 'punctuation'
+ },
+ 'url-reference': {
+ // [id]: http://example.com "Optional title"
+ // [id]: http://example.com 'Optional title'
+ // [id]: http://example.com (Optional title)
+ // [id]: "Optional title"
+ pattern: /!?\[[^\]]+\]:[\t ]+(?:\S+|<(?:\\.|[^>\\])+>)(?:[\t ]+(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\)))?/,
+ inside: {
+ 'variable': {
+ pattern: /^(!?\[)[^\]]+/,
+ lookbehind: true
+ },
+ 'string': /(?:"(?:\\.|[^"\\])*"|'(?:\\.|[^'\\])*'|\((?:\\.|[^)\\])*\))$/,
+ 'punctuation': /^[\[\]!:]|[<>]/
+ },
+ alias: 'url'
+ },
+ 'bold': {
+ // **strong**
+ // __strong__
+
+ // Allow only one line break
+ pattern: /(^|[^\\])(\*\*|__)(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,
+ lookbehind: true,
+ inside: {
+ 'punctuation': /^\*\*|^__|\*\*$|__$/
+ }
+ },
+ 'italic': {
+ // *em*
+ // _em_
+
+ // Allow only one line break
+ pattern: /(^|[^\\])([*_])(?:(?:\r?\n|\r)(?!\r?\n|\r)|.)+?\2/,
+ lookbehind: true,
+ inside: {
+ 'punctuation': /^[*_]|[*_]$/
+ }
+ },
+ 'url': {
+ // [example](http://example.com "Optional title")
+ // [example] [id]
+ pattern: /!?\[[^\]]+\](?:\([^\s)]+(?:[\t ]+"(?:\\.|[^"\\])*")?\)| ?\[[^\]\n]*\])/,
+ inside: {
+ 'variable': {
+ pattern: /(!?\[)[^\]]+(?=\]$)/,
+ lookbehind: true
+ },
+ 'string': {
+ pattern: /"(?:\\.|[^"\\])*"(?=\)$)/
+ }
+ }
+ }
+};
+
+export default marks;