diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js
index bee629f6..98ee30d3 100644
--- a/browser/components/MarkdownPreview.js
+++ b/browser/components/MarkdownPreview.js
@@ -1,16 +1,34 @@
-var React = require('react')
-var { PropTypes } = React
+import React, { PropTypes } from 'react'
import markdown from '../lib/markdown'
-var ReactDOM = require('react-dom')
+import ReactDOM from 'react-dom'
+import sanitizeHtml from '@rokt33r/sanitize-html'
+import hljs from 'highlight.js'
const electron = require('electron')
const shell = electron.shell
const katex = window.katex
+const sanitizeOpts = {
+ allowedTags: [ 'h1', 'h2', 'h3', 'h4', 'h5', 'h6', 'blockquote', 'p', 'a', 'ul', 'ol',
+ 'nl', 'li', 'b', 'i', 'strong', 'em', 'strike', 'code', 'hr', 'br', 'div',
+ 'table', 'thead', 'caption', 'tbody', 'tr', 'th', 'td', 'pre', 'img', 'span', 'cite', 'del', 'u' ],
+ allowedClasses: {
+ 'a': ['lineAnchor'],
+ 'div': ['math'],
+ 'span': ['math', 'hljs-*'],
+ 'code': ['language-*']
+ },
+ allowedAttributes: {
+ a: ['href', 'data-key'],
+ img: [ 'src' ]
+ }
+}
+
function handleAnchorClick (e) {
e.preventDefault()
e.stopPropagation()
+ console.log(e.target.href)
shell.openExternal(e.target.href)
}
@@ -34,11 +52,13 @@ function math2Katex (display) {
export default class MarkdownPreview extends React.Component {
componentDidMount () {
this.addListener()
+ // this.renderCode()
this.renderMath()
}
componentDidUpdate () {
this.addListener()
+ // this.renderCode()
this.renderMath()
}
@@ -50,6 +70,20 @@ export default class MarkdownPreview extends React.Component {
this.removeListener()
}
+ renderCode () {
+ let codes = ReactDOM.findDOMNode(this).querySelectorAll('code:not(.rendered)')
+ Array.prototype.forEach.call(codes, el => {
+ let matched = el.className.match(/language-(.+)/)
+ let lang = matched ? matched[1] : null
+ try {
+ let result = hljs.highlight(lang, el.innerHTML)
+ el.innerHTML = result.value
+ el.className += ' rendered'
+ } catch (e) {
+ }
+ })
+ }
+
renderMath () {
let inline = ReactDOM.findDOMNode(this).querySelectorAll('span.math')
Array.prototype.forEach.call(inline, math2Katex(false))
@@ -112,6 +146,9 @@ export default class MarkdownPreview extends React.Component {
let content = isEmpty
? '(Empty content)'
: this.props.content
+ content = markdown(content)
+ content = sanitizeHtml(content, sanitizeOpts)
+
return (
this.handleMouseDown(e)}
onMouseMove={e => this.handleMouseMove(e)}
onMouseUp={e => this.handleMouseUp(e)}
- dangerouslySetInnerHTML={{__html: ' ' + markdown(content)}}
+ dangerouslySetInnerHTML={{__html: ' ' + content}}
/>
)
}
diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js
index e3192fa5..3ba78b5c 100644
--- a/browser/lib/markdown.js
+++ b/browser/lib/markdown.js
@@ -1,23 +1,20 @@
import markdownit from 'markdown-it'
-import hljs from 'highlight.js'
import emoji from 'markdown-it-emoji'
import math from 'markdown-it-math'
+import hljs from 'highlight.js'
var md = markdownit({
typographer: true,
linkify: true,
+ html: true,
+ xhtmlOut: true,
highlight: function (str, lang) {
if (lang && hljs.getLanguage(lang)) {
try {
return hljs.highlight(lang, str).value
- } catch (__) {}
+ } catch (e) {}
}
-
- try {
- return hljs.highlightAuto(str).value
- } catch (__) {}
-
- return ''
+ return str
}
})
md.use(emoji)
@@ -33,6 +30,7 @@ md.use(math, {
let originalRenderToken = md.renderer.renderToken
md.renderer.renderToken = function renderToken (tokens, idx, options) {
let token = tokens[idx]
+
let result = originalRenderToken.call(md.renderer, tokens, idx, options)
if (token.map != null) {
return result + '
'
diff --git a/browser/main/HomePage/ArticleDetail/ArticleEditor.js b/browser/main/HomePage/ArticleDetail/ArticleEditor.js
index 7538ed60..66af881d 100644
--- a/browser/main/HomePage/ArticleDetail/ArticleEditor.js
+++ b/browser/main/HomePage/ArticleDetail/ArticleEditor.js
@@ -56,7 +56,8 @@ export default class ArticleEditor extends React.Component {
}
render () {
- if (this.props.mode === 'markdown' && this.state.status === PREVIEW_MODE) {
+ let showPreview = this.props.mode === 'markdown' && this.state.status === PREVIEW_MODE
+ if (showPreview) {
return (
this.handleUnsavedItemClick(combinedArticle)(e)} className={className}>
- {combinedArticle.title}
+ {combinedArticle.title.trim().length > 0
+ ? combinedArticle.title
+ : (Untitled)}
diff --git a/browser/main/HomePage/index.js b/browser/main/HomePage/index.js
index c46cb830..cb67fefb 100644
--- a/browser/main/HomePage/index.js
+++ b/browser/main/HomePage/index.js
@@ -144,9 +144,8 @@ function remap (state) {
articles.sort((a, b) => {
let match = new Date(b.updatedAt) - new Date(a.updatedAt)
- if (match === 0) match = new Date(b.createdAt) - new Date(a.createdAt)
- if (match === 0) match = b.title.compare(a.title)
- if (match === 0) match = b.key.compare(a.key)
+ if (match === 0) match = b.title.localeCompare(a.title)
+ if (match === 0) match = b.key.localeCompare(a.key)
return match
})
let allArticles = articles.slice()
diff --git a/browser/styles/mixins/marked.styl b/browser/styles/mixins/marked.styl
index afbfe684..4d34005e 100644
--- a/browser/styles/mixins/marked.styl
+++ b/browser/styles/mixins/marked.styl
@@ -26,6 +26,7 @@ marked()
padding 0
margin 0
display inline
+ font-size 0
hr
border-top none
border-bottom solid 1px borderColor
@@ -33,7 +34,7 @@ marked()
h1, h2, h3, h4, h5, h6
margin 0 0 15px
font-weight 600
- * + h1, * + h2, * + h3, * + h4, * + h5, * + h6
+ *:not(a.lineAnchor) + h1, *:not(a.lineAnchor) + h2, *:not(a.lineAnchor) + h3, *:not(a.lineAnchor) + h4, *:not(a.lineAnchor) + h5, *:not(a.lineAnchor) + h6
margin-top 25px
h1
font-size 2em
@@ -55,26 +56,28 @@ marked()
font-size 0.8em
line-height 1em
- * + p, * + blockquote, * + ul, * + ol, * + pre
+ *:not(a.lineAnchor) + p, *:not(a.lineAnchor) + blockquote, *:not(a.lineAnchor) + ul, *:not(a.lineAnchor) + ol, *:not(a.lineAnchor) + pre
margin-top 15px
p
line-height 1.9em
margin 0 0 15px
img
max-width 100%
- strong
+ strong, b
font-weight bold
- em
+ em, i
font-style italic
s
text-decoration line-through
+ u
+ text-decoration underline
blockquote
border-left solid 4px brandBorderColor
margin 0 0 15px
padding 0 25px
ul
list-style-type disc
- padding-left 35px
+ padding-left 25px
margin-bottom 15px
li
display list-item
@@ -87,7 +90,7 @@ marked()
list-style-type square
ol
list-style-type decimal
- padding-left 35px
+ padding-left 25px
margin-bottom 15px
li
display list-item
@@ -97,18 +100,18 @@ marked()
code
font-family Monaco, Menlo, 'Ubuntu Mono', Consolas, source-code-pro, monospace
padding 2px 4px
- border solid 1px borderColor
+ border solid 1px alpha(borderColor, 0.3)
border-radius 4px
font-size 0.9em
color black
text-decoration none
background-color #F6F6F6
margin-right 2px
- * + code
+ *:not(a.lineAnchor) + code
margin-left 2px
pre
padding 5px
- border solid 1px borderColor
+ border solid 1px alpha(borderColor, 0.3)
border-radius 5px
overflow-x auto
margin 0 0 15px
diff --git a/package.json b/package.json
index 27cc6add..20c6f35c 100644
--- a/package.json
+++ b/package.json
@@ -31,6 +31,7 @@
"homepage": "https://github.com/Rokt33r/codexen-app#readme",
"dependencies": {
"@rokt33r/node-ipc": "^5.0.4",
+ "@rokt33r/sanitize-html": "^1.11.2",
"devicon": "^2.0.0",
"electron-gh-releases": "^2.0.2",
"font-awesome": "^4.3.0",
diff --git a/resources/fonts/Lato-Regular.ttf b/resources/fonts/Lato-Regular.ttf
new file mode 100644
index 00000000..74decd9e
Binary files /dev/null and b/resources/fonts/Lato-Regular.ttf differ
diff --git a/resources/fonts/Lato-Regular.woff b/resources/fonts/Lato-Regular.woff
new file mode 100644
index 00000000..ae1307ff
Binary files /dev/null and b/resources/fonts/Lato-Regular.woff differ
diff --git a/resources/fonts/Lato-Regular.woff2 b/resources/fonts/Lato-Regular.woff2
new file mode 100644
index 00000000..3bf98433
Binary files /dev/null and b/resources/fonts/Lato-Regular.woff2 differ
diff --git a/webpack-skeleton.js b/webpack-skeleton.js
index 55eeb57a..b9b21fdf 100644
--- a/webpack-skeleton.js
+++ b/webpack-skeleton.js
@@ -31,7 +31,8 @@ var config = {
'highlight.js',
'markdown-it-emoji',
'fs-jetpack',
- 'markdown-it-math'
+ 'markdown-it-math',
+ '@rokt33r/sanitize-html'
]
}