1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 17:56:25 +00:00

added support for toggling smart quotes in preview

This commit is contained in:
Yu-Hung Ou
2018-03-02 00:06:58 +11:00
parent 6bcb6398f8
commit e89c0a3e61
6 changed files with 197 additions and 167 deletions

View File

@@ -279,6 +279,7 @@ class MarkdownEditor extends React.Component {
lineNumber={config.preview.lineNumber} lineNumber={config.preview.lineNumber}
indentSize={editorIndentSize} indentSize={editorIndentSize}
scrollPastEnd={config.preview.scrollPastEnd} scrollPastEnd={config.preview.scrollPastEnd}
smartQuotes={config.preview.smartQuotes}
ref='preview' ref='preview'
onContextMenu={(e) => this.handleContextMenu(e)} onContextMenu={(e) => this.handleContextMenu(e)}
onDoubleClick={(e) => this.handleDoubleClick(e)} onDoubleClick={(e) => this.handleDoubleClick(e)}

View File

@@ -1,6 +1,6 @@
import PropTypes from 'prop-types' import PropTypes from 'prop-types'
import React from 'react' import React from 'react'
import markdown from 'browser/lib/markdown' import Markdown from 'browser/lib/markdown'
import _ from 'lodash' import _ from 'lodash'
import CodeMirror from 'codemirror' import CodeMirror from 'codemirror'
import 'codemirror-mode-elixir' import 'codemirror-mode-elixir'
@@ -130,6 +130,13 @@ export default class MarkdownPreview extends React.Component {
this.printHandler = () => this.handlePrint() this.printHandler = () => this.handlePrint()
this.linkClickHandler = this.handlelinkClick.bind(this) this.linkClickHandler = this.handlelinkClick.bind(this)
this.initMarkdown = this.initMarkdown.bind(this)
this.initMarkdown()
}
initMarkdown () {
const { smartQuotes } = this.props
this.markdown = new Markdown({ typographer: smartQuotes })
} }
handlePreviewAnchorClick (e) { handlePreviewAnchorClick (e) {
@@ -198,7 +205,7 @@ export default class MarkdownPreview extends React.Component {
const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme} = this.getStyleParams() const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme} = this.getStyleParams()
const inlineStyles = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, lineNumber) const inlineStyles = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, lineNumber)
const body = markdown.render(noteContent) const body = this.markdown.render(noteContent)
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES] const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
files.forEach((file) => { files.forEach((file) => {
@@ -309,6 +316,10 @@ export default class MarkdownPreview extends React.Component {
componentDidUpdate (prevProps) { componentDidUpdate (prevProps) {
if (prevProps.value !== this.props.value) this.rewriteIframe() if (prevProps.value !== this.props.value) this.rewriteIframe()
if (prevProps.smartQuotes !== this.props.smartQuotes) {
this.initMarkdown()
this.rewriteIframe()
}
if (prevProps.fontFamily !== this.props.fontFamily || if (prevProps.fontFamily !== this.props.fontFamily ||
prevProps.fontSize !== this.props.fontSize || prevProps.fontSize !== this.props.fontSize ||
prevProps.codeBlockFontFamily !== this.props.codeBlockFontFamily || prevProps.codeBlockFontFamily !== this.props.codeBlockFontFamily ||
@@ -374,7 +385,7 @@ export default class MarkdownPreview extends React.Component {
value = value.replace(codeBlock, htmlTextHelper.encodeEntities(codeBlock)) value = value.replace(codeBlock, htmlTextHelper.encodeEntities(codeBlock))
}) })
} }
this.refs.root.contentWindow.document.body.innerHTML = markdown.render(value) this.refs.root.contentWindow.document.body.innerHTML = this.markdown.render(value)
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => { _.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
this.fixDecodedURI(el) this.fixDecodedURI(el)
@@ -390,9 +401,9 @@ export default class MarkdownPreview extends React.Component {
}) })
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('img'), (el) => { _.forEach(this.refs.root.contentWindow.document.querySelectorAll('img'), (el) => {
el.src = markdown.normalizeLinkText(el.src) el.src = this.markdown.normalizeLinkText(el.src)
if (!/\/:storage/.test(el.src)) return if (!/\/:storage/.test(el.src)) return
el.src = `file:///${markdown.normalizeLinkText(path.join(storagePath, 'images', path.basename(el.src)))}` el.src = `file:///${this.markdown.normalizeLinkText(path.join(storagePath, 'images', path.basename(el.src)))}`
}) })
codeBlockTheme = consts.THEMES.some((_theme) => _theme === codeBlockTheme) codeBlockTheme = consts.THEMES.some((_theme) => _theme === codeBlockTheme)
@@ -533,5 +544,6 @@ MarkdownPreview.propTypes = {
className: PropTypes.string, className: PropTypes.string,
value: PropTypes.string, value: PropTypes.string,
showCopyNotification: PropTypes.bool, showCopyNotification: PropTypes.bool,
storagePath: PropTypes.string storagePath: PropTypes.string,
smartQuotes: PropTypes.bool
} }

View File

@@ -127,6 +127,7 @@ class MarkdownSplitEditor extends React.Component {
codeBlockFontFamily={config.editor.fontFamily} codeBlockFontFamily={config.editor.fontFamily}
lineNumber={config.preview.lineNumber} lineNumber={config.preview.lineNumber}
scrollPastEnd={config.preview.scrollPastEnd} scrollPastEnd={config.preview.scrollPastEnd}
smartQuotes={config.preview.smartQuotes}
ref='preview' ref='preview'
tabInde='0' tabInde='0'
value={value} value={value}

View File

@@ -19,7 +19,9 @@ function createGutter (str, firstLineNumber) {
return '<span class="lineNumber CodeMirror-gutters">' + lines.join('') + '</span>' return '<span class="lineNumber CodeMirror-gutters">' + lines.join('') + '</span>'
} }
var md = markdownit({ class Markdown {
constructor (options = {}) {
const defaultOptions = {
typographer: true, typographer: true,
linkify: true, linkify: true,
html: true, html: true,
@@ -45,11 +47,14 @@ var md = markdownit({
str + str +
'</code></pre>' '</code></pre>'
} }
}) }
md.use(emoji, {
const updatedOptions = Object.assign(defaultOptions, options)
this.md = markdownit(updatedOptions)
this.md.use(emoji, {
shortcuts: {} shortcuts: {}
}) })
md.use(math, { this.md.use(math, {
inlineOpen: config.preview.latexInlineOpen, inlineOpen: config.preview.latexInlineOpen,
inlineClose: config.preview.latexInlineClose, inlineClose: config.preview.latexInlineClose,
blockOpen: config.preview.latexBlockOpen, blockOpen: config.preview.latexBlockOpen,
@@ -72,22 +77,22 @@ md.use(math, {
} }
return output return output
} }
}) })
md.use(require('markdown-it-imsize')) this.md.use(require('markdown-it-imsize'))
md.use(require('markdown-it-footnote')) this.md.use(require('markdown-it-footnote'))
md.use(require('markdown-it-multimd-table')) this.md.use(require('markdown-it-multimd-table'))
md.use(require('markdown-it-named-headers'), { this.md.use(require('markdown-it-named-headers'), {
slugify: (header) => { slugify: (header) => {
return encodeURI(header.trim() return encodeURI(header.trim()
.replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '') .replace(/[\]\[\!\"\#\$\%\&\'\(\)\*\+\,\.\/\:\;\<\=\>\?\@\\\^\_\{\|\}\~]/g, '')
.replace(/\s+/g, '-')) .replace(/\s+/g, '-'))
.replace(/\-+$/, '') .replace(/\-+$/, '')
} }
}) })
md.use(require('markdown-it-kbd')) this.md.use(require('markdown-it-kbd'))
const deflate = require('markdown-it-plantuml/lib/deflate') const deflate = require('markdown-it-plantuml/lib/deflate')
md.use(require('markdown-it-plantuml'), '', { this.md.use(require('markdown-it-plantuml'), '', {
generateSource: function (umlCode) { generateSource: function (umlCode) {
const s = unescape(encodeURIComponent(umlCode)) const s = unescape(encodeURIComponent(umlCode))
const zippedCode = deflate.encode64( const zippedCode = deflate.encode64(
@@ -95,10 +100,10 @@ md.use(require('markdown-it-plantuml'), '', {
) )
return `http://www.plantuml.com/plantuml/svg/${zippedCode}` return `http://www.plantuml.com/plantuml/svg/${zippedCode}`
} }
}) })
// Override task item // Override task item
md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) { this.md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
let content, terminate, i, l, token let content, terminate, i, l, token
let nextLine = startLine + 1 let nextLine = startLine + 1
const terminatorRules = state.md.block.ruler.getRules('paragraph') const terminatorRules = state.md.block.ruler.getRules('paragraph')
@@ -153,11 +158,11 @@ md.block.ruler.at('paragraph', function (state, startLine/*, endLine */) {
token = state.push('paragraph_close', 'p', -1) token = state.push('paragraph_close', 'p', -1)
return true return true
}) })
// Add line number attribute for scrolling // Add line number attribute for scrolling
const originalRender = md.renderer.render const originalRender = this.md.renderer.render
md.renderer.render = function render (tokens, options, env) { this.md.renderer.render = (tokens, options, env) => {
tokens.forEach((token) => { tokens.forEach((token) => {
switch (token.type) { switch (token.type) {
case 'heading_open': case 'heading_open':
@@ -167,23 +172,22 @@ md.renderer.render = function render (tokens, options, env) {
token.attrPush(['data-line', token.map[0]]) token.attrPush(['data-line', token.map[0]])
} }
}) })
const result = originalRender.call(md.renderer, tokens, options, env) const result = originalRender.call(this.md.renderer, tokens, options, env)
return result return result
} }
// FIXME We should not depend on global variable. // FIXME We should not depend on global variable.
window.md = md window.md = this.md
}
function normalizeLinkText (linkText) { render (content) {
return md.normalizeLinkText(linkText)
}
const markdown = {
render: function markdown (content) {
if (!_.isString(content)) content = '' if (!_.isString(content)) content = ''
const renderedContent = md.render(content) return this.md.render(content)
return renderedContent }
},
normalizeLinkText normalizeLinkText (linkText) {
return this.md.normalizeLinkText(linkText)
}
} }
export default markdown export default Markdown

View File

@@ -48,7 +48,8 @@ export const DEFAULT_CONFIG = {
latexInlineClose: '$', latexInlineClose: '$',
latexBlockOpen: '$$', latexBlockOpen: '$$',
latexBlockClose: '$$', latexBlockClose: '$$',
scrollPastEnd: false scrollPastEnd: false,
smartQuotes: true
} }
} }

View File

@@ -88,7 +88,8 @@ class UiTab extends React.Component {
latexInlineClose: this.refs.previewLatexInlineClose.value, latexInlineClose: this.refs.previewLatexInlineClose.value,
latexBlockOpen: this.refs.previewLatexBlockOpen.value, latexBlockOpen: this.refs.previewLatexBlockOpen.value,
latexBlockClose: this.refs.previewLatexBlockClose.value, latexBlockClose: this.refs.previewLatexBlockClose.value,
scrollPastEnd: this.refs.previewScrollPastEnd.checked scrollPastEnd: this.refs.previewScrollPastEnd.checked,
smartQuotes: this.refs.previewSmartQuotes.checked
} }
} }
@@ -402,6 +403,16 @@ class UiTab extends React.Component {
Show line numbers for preview code blocks Show line numbers for preview code blocks
</label> </label>
</div> </div>
<div styleName='group-checkBoxSection'>
<label>
<input onChange={(e) => this.handleUIChange(e)}
checked={this.state.config.preview.smartQuotes}
ref='previewSmartQuotes'
type='checkbox'
/>&nbsp;
Enable smart quotes
</label>
</div>
<div styleName='group-section'> <div styleName='group-section'>
<div styleName='group-section-label'> <div styleName='group-section-label'>
LaTeX Inline Open Delimiter LaTeX Inline Open Delimiter