1
0
mirror of https://github.com/BoostIo/Boostnote synced 2025-12-13 01:36:22 +00:00

Fixing flickering in both cases

This commit is contained in:
Max Buranbaev
2018-06-27 13:07:38 +05:00
parent 6401016424
commit 5de176757d
6 changed files with 673 additions and 383 deletions

View File

@@ -24,15 +24,24 @@ const path = require('path')
const dialog = remote.dialog
const markdownStyle = require('!!css!stylus?sourceMap!./markdown.styl')[0][1]
const appPath = 'file://' + (process.env.NODE_ENV === 'production'
? app.getAppPath()
: path.resolve())
const appPath =
'file://' +
(process.env.NODE_ENV === 'production' ? app.getAppPath() : path.resolve())
const CSS_FILES = [
`${appPath}/node_modules/katex/dist/katex.min.css`,
`${appPath}/node_modules/codemirror/lib/codemirror.css`
]
function buildStyle (fontFamily, fontSize, codeBlockFontFamily, lineNumber, scrollPastEnd, theme, allowCustomCSS, customCSS) {
function buildStyle (
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
) {
return `
@font-face {
font-family: 'Lato';
@@ -137,17 +146,29 @@ if (!OSX) {
defaultFontFamily.unshift('Microsoft YaHei')
defaultFontFamily.unshift('meiryo')
}
const defaultCodeBlockFontFamily = ['Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', 'monospace']
const defaultCodeBlockFontFamily = [
'Monaco',
'Menlo',
'Ubuntu Mono',
'Consolas',
'source-code-pro',
'monospace'
]
export default class MarkdownPreview extends React.Component {
constructor (props) {
super(props)
this.contextMenuHandler = (e) => this.handleContextMenu(e)
this.mouseDownHandler = (e) => this.handleMouseDown(e)
this.mouseUpHandler = (e) => this.handleMouseUp(e)
this.DoubleClickHandler = (e) => this.handleDoubleClick(e)
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {leading: false, trailing: true})
this.checkboxClickHandler = (e) => this.handleCheckboxClick(e)
this.state = {
isReady: false
}
this.contextMenuHandler = e => this.handleContextMenu(e)
this.mouseDownHandler = e => this.handleMouseDown(e)
this.mouseUpHandler = e => this.handleMouseUp(e)
this.DoubleClickHandler = e => this.handleDoubleClick(e)
this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {
leading: false,
trailing: true
})
this.checkboxClickHandler = e => this.handleCheckboxClick(e)
this.saveAsTextHandler = () => this.handleSaveAsText()
this.saveAsMdHandler = () => this.handleSaveAsMd()
this.saveAsHtmlHandler = () => this.handleSaveAsHtml()
@@ -214,31 +235,56 @@ export default class MarkdownPreview extends React.Component {
handleSaveAsHtml () {
this.exportAsDocument('html', (noteContent, exportTasks) => {
const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, scrollPastEnd, theme, allowCustomCSS, customCSS} = this.getStyleParams()
const {
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
codeBlockTheme,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
} = this.getStyleParams()
const inlineStyles = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, scrollPastEnd, theme, allowCustomCSS, customCSS)
const inlineStyles = buildStyle(
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
)
let body = this.markdown.render(escapeHtmlCharacters(noteContent))
const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES]
const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent(noteContent, this.props.storagePath)
const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent(
noteContent,
this.props.storagePath
)
files.forEach((file) => {
files.forEach(file => {
file = file.replace('file://', '')
exportTasks.push({
src: file,
dst: 'css'
})
})
attachmentsAbsolutePaths.forEach((attachment) => {
attachmentsAbsolutePaths.forEach(attachment => {
exportTasks.push({
src: attachment,
dst: attachmentManagement.DESTINATION_FOLDER
})
})
body = attachmentManagement.removeStorageAndNoteReferences(body, this.props.noteKey)
body = attachmentManagement.removeStorageAndNoteReferences(
body,
this.props.noteKey
)
let styles = ''
files.forEach((file) => {
files.forEach(file => {
styles += `<link rel="stylesheet" href="css/${path.basename(file)}">`
})
@@ -260,42 +306,51 @@ export default class MarkdownPreview extends React.Component {
exportAsDocument (fileType, contentFormatter) {
const options = {
filters: [
{name: 'Documents', extensions: [fileType]}
],
filters: [{ name: 'Documents', extensions: [fileType] }],
properties: ['openFile', 'createDirectory']
}
dialog.showSaveDialog(remote.getCurrentWindow(), options,
(filename) => {
if (filename) {
const content = this.props.value
const storage = this.props.storagePath
dialog.showSaveDialog(remote.getCurrentWindow(), options, filename => {
if (filename) {
const content = this.props.value
const storage = this.props.storagePath
exportNote(storage, content, filename, contentFormatter)
.then((res) => {
dialog.showMessageBox(remote.getCurrentWindow(), {type: 'info', message: `Exported to ${filename}`})
}).catch((err) => {
dialog.showErrorBox('Export error', err ? err.message || err : 'Unexpected error during export')
throw err
})
}
})
exportNote(storage, content, filename, contentFormatter)
.then(res => {
dialog.showMessageBox(remote.getCurrentWindow(), {
type: 'info',
message: `Exported to ${filename}`
})
})
.catch(err => {
dialog.showErrorBox(
'Export error',
err ? err.message || err : 'Unexpected error during export'
)
throw err
})
}
})
}
fixDecodedURI (node) {
if (node && node.children.length === 1 && typeof node.children[0] === 'string') {
if (
node &&
node.children.length === 1 &&
typeof node.children[0] === 'string'
) {
const { innerText, href } = node
node.innerText = mdurl.decode(href) === innerText
? href
: innerText
node.innerText = mdurl.decode(href) === innerText ? href : innerText
}
}
componentDidMount () {
this.refs.root.setAttribute('sandbox', 'allow-scripts')
this.refs.root.contentWindow.document.body.addEventListener('contextmenu', this.contextMenuHandler)
this.refs.root.contentWindow.document.body.addEventListener(
'contextmenu',
this.contextMenuHandler
)
let styles = `
<style id='style'></style>
@@ -303,7 +358,7 @@ export default class MarkdownPreview extends React.Component {
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
`
CSS_FILES.forEach((file) => {
CSS_FILES.forEach(file => {
styles += `<link rel="stylesheet" href="${file}">`
})
@@ -311,26 +366,66 @@ export default class MarkdownPreview extends React.Component {
this.rewriteIframe()
this.applyStyle()
this.refs.root.contentWindow.document.addEventListener('mousedown', this.mouseDownHandler)
this.refs.root.contentWindow.document.addEventListener('mouseup', this.mouseUpHandler)
this.refs.root.contentWindow.document.addEventListener('dblclick', this.DoubleClickHandler)
this.refs.root.contentWindow.document.addEventListener('drop', this.preventImageDroppedHandler)
this.refs.root.contentWindow.document.addEventListener('dragover', this.preventImageDroppedHandler)
this.refs.root.contentWindow.document.addEventListener('scroll', this.scrollHandler)
this.refs.root.contentWindow.document.addEventListener(
'mousedown',
this.mouseDownHandler
)
this.refs.root.contentWindow.document.addEventListener(
'mouseup',
this.mouseUpHandler
)
this.refs.root.contentWindow.document.addEventListener(
'dblclick',
this.DoubleClickHandler
)
this.refs.root.contentWindow.document.addEventListener(
'drop',
this.preventImageDroppedHandler
)
this.refs.root.contentWindow.document.addEventListener(
'dragover',
this.preventImageDroppedHandler
)
this.refs.root.contentWindow.document.addEventListener(
'scroll',
this.scrollHandler
)
eventEmitter.on('export:save-text', this.saveAsTextHandler)
eventEmitter.on('export:save-md', this.saveAsMdHandler)
eventEmitter.on('export:save-html', this.saveAsHtmlHandler)
eventEmitter.on('print', this.printHandler)
setTimeout(() => this.setState({ isReady: true }))
}
componentWillUnmount () {
this.refs.root.contentWindow.document.body.removeEventListener('contextmenu', this.contextMenuHandler)
this.refs.root.contentWindow.document.removeEventListener('mousedown', this.mouseDownHandler)
this.refs.root.contentWindow.document.removeEventListener('mouseup', this.mouseUpHandler)
this.refs.root.contentWindow.document.removeEventListener('dblclick', this.DoubleClickHandler)
this.refs.root.contentWindow.document.removeEventListener('drop', this.preventImageDroppedHandler)
this.refs.root.contentWindow.document.removeEventListener('dragover', this.preventImageDroppedHandler)
this.refs.root.contentWindow.document.removeEventListener('scroll', this.scrollHandler)
this.refs.root.contentWindow.document.body.removeEventListener(
'contextmenu',
this.contextMenuHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'mousedown',
this.mouseDownHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'mouseup',
this.mouseUpHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'dblclick',
this.DoubleClickHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'drop',
this.preventImageDroppedHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'dragover',
this.preventImageDroppedHandler
)
this.refs.root.contentWindow.document.removeEventListener(
'scroll',
this.scrollHandler
)
eventEmitter.off('export:save-text', this.saveAsTextHandler)
eventEmitter.off('export:save-md', this.saveAsMdHandler)
eventEmitter.off('export:save-html', this.saveAsHtmlHandler)
@@ -339,14 +434,17 @@ export default class MarkdownPreview extends React.Component {
componentDidUpdate (prevProps) {
if (prevProps.value !== this.props.value) this.rewriteIframe()
if (prevProps.smartQuotes !== this.props.smartQuotes ||
prevProps.sanitize !== this.props.sanitize ||
prevProps.smartArrows !== this.props.smartArrows ||
prevProps.breaks !== this.props.breaks) {
if (
prevProps.smartQuotes !== this.props.smartQuotes ||
prevProps.sanitize !== this.props.sanitize ||
prevProps.smartArrows !== this.props.smartArrows ||
prevProps.breaks !== this.props.breaks
) {
this.initMarkdown()
this.rewriteIframe()
}
if (prevProps.fontFamily !== this.props.fontFamily ||
if (
prevProps.fontFamily !== this.props.fontFamily ||
prevProps.fontSize !== this.props.fontSize ||
prevProps.codeBlockFontFamily !== this.props.codeBlockFontFamily ||
prevProps.codeBlockTheme !== this.props.codeBlockTheme ||
@@ -355,34 +453,82 @@ export default class MarkdownPreview extends React.Component {
prevProps.theme !== this.props.theme ||
prevProps.scrollPastEnd !== this.props.scrollPastEnd ||
prevProps.allowCustomCSS !== this.props.allowCustomCSS ||
prevProps.customCSS !== this.props.customCSS) {
prevProps.customCSS !== this.props.customCSS
) {
this.applyStyle()
this.rewriteIframe()
}
}
getStyleParams () {
const { fontSize, lineNumber, codeBlockTheme, scrollPastEnd, theme, allowCustomCSS, customCSS } = this.props
const {
fontSize,
lineNumber,
codeBlockTheme,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
} = this.props
let { fontFamily, codeBlockFontFamily } = this.props
fontFamily = _.isString(fontFamily) && fontFamily.trim().length > 0
? fontFamily.split(',').map(fontName => fontName.trim()).concat(defaultFontFamily)
: defaultFontFamily
codeBlockFontFamily = _.isString(codeBlockFontFamily) && codeBlockFontFamily.trim().length > 0
? codeBlockFontFamily.split(',').map(fontName => fontName.trim()).concat(defaultCodeBlockFontFamily)
: defaultCodeBlockFontFamily
? fontFamily
.split(',')
.map(fontName => fontName.trim())
.concat(defaultFontFamily)
: defaultFontFamily
codeBlockFontFamily = _.isString(codeBlockFontFamily) &&
codeBlockFontFamily.trim().length > 0
? codeBlockFontFamily
.split(',')
.map(fontName => fontName.trim())
.concat(defaultCodeBlockFontFamily)
: defaultCodeBlockFontFamily
return {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, scrollPastEnd, theme, allowCustomCSS, customCSS}
return {
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
codeBlockTheme,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
}
}
applyStyle () {
const {fontFamily, fontSize, codeBlockFontFamily, lineNumber, codeBlockTheme, scrollPastEnd, theme, allowCustomCSS, customCSS} = this.getStyleParams()
const {
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
codeBlockTheme,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
} = this.getStyleParams()
this.getWindow().document.getElementById('codeTheme').href = this.GetCodeThemeLink(codeBlockTheme)
this.getWindow().document.getElementById('style').innerHTML = buildStyle(fontFamily, fontSize, codeBlockFontFamily, lineNumber, scrollPastEnd, theme, allowCustomCSS, customCSS)
this.getWindow().document.getElementById(
'codeTheme'
).href = this.GetCodeThemeLink(codeBlockTheme)
this.getWindow().document.getElementById('style').innerHTML = buildStyle(
fontFamily,
fontSize,
codeBlockFontFamily,
lineNumber,
scrollPastEnd,
theme,
allowCustomCSS,
customCSS
)
}
GetCodeThemeLink (theme) {
theme = consts.THEMES.some((_theme) => _theme === theme) && theme !== 'default'
theme = consts.THEMES.some(_theme => _theme === theme) &&
theme !== 'default'
? theme
: 'elegant'
return theme.startsWith('solarized')
@@ -391,71 +537,103 @@ export default class MarkdownPreview extends React.Component {
}
rewriteIframe () {
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('input[type="checkbox"]'), (el) => {
el.removeEventListener('click', this.checkboxClickHandler)
})
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll(
'input[type="checkbox"]'
),
el => {
el.removeEventListener('click', this.checkboxClickHandler)
}
)
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
el.removeEventListener('click', this.linkClickHandler)
})
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('a'),
el => {
el.removeEventListener('click', this.linkClickHandler)
}
)
const { theme, indentSize, showCopyNotification, storagePath, noteKey } = this.props
const {
theme,
indentSize,
showCopyNotification,
storagePath,
noteKey
} = this.props
let { value, codeBlockTheme } = this.props
this.refs.root.contentWindow.document.body.setAttribute('data-theme', theme)
const codeBlocks = value.match(/(```)(.|[\n])*?(```)/g)
if (codeBlocks !== null) {
codeBlocks.forEach((codeBlock) => {
value = value.replace(codeBlock, htmlTextHelper.encodeEntities(codeBlock))
codeBlocks.forEach(codeBlock => {
value = value.replace(
codeBlock,
htmlTextHelper.encodeEntities(codeBlock)
)
})
}
let renderedHTML = this.markdown.render(value)
attachmentManagement.migrateAttachments(renderedHTML, storagePath, noteKey)
this.refs.root.contentWindow.document.body.innerHTML = attachmentManagement.fixLocalURLS(renderedHTML, storagePath)
this.refs.root.contentWindow.document.body.innerHTML = attachmentManagement.fixLocalURLS(
renderedHTML,
storagePath
)
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('input[type="checkbox"]'), (el) => {
el.addEventListener('click', this.checkboxClickHandler)
})
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll(
'input[type="checkbox"]'
),
el => {
el.addEventListener('click', this.checkboxClickHandler)
}
)
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('a'), (el) => {
this.fixDecodedURI(el)
el.addEventListener('click', this.linkClickHandler)
})
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('a'),
el => {
this.fixDecodedURI(el)
el.addEventListener('click', this.linkClickHandler)
}
)
codeBlockTheme = consts.THEMES.some((_theme) => _theme === codeBlockTheme)
codeBlockTheme = consts.THEMES.some(_theme => _theme === codeBlockTheme)
? codeBlockTheme
: 'default'
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.code code'), (el) => {
let syntax = CodeMirror.findModeByName(convertModeName(el.className))
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
CodeMirror.requireMode(syntax.mode, () => {
const content = htmlTextHelper.decodeEntities(el.innerHTML)
const copyIcon = document.createElement('i')
copyIcon.innerHTML = '<button class="clipboardButton"><svg width="13" height="13" viewBox="0 0 1792 1792" ><path d="M768 1664h896v-640h-416q-40 0-68-28t-28-68v-416h-384v1152zm256-1440v-64q0-13-9.5-22.5t-22.5-9.5h-704q-13 0-22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h704q13 0 22.5-9.5t9.5-22.5zm256 672h299l-299-299v299zm512 128v672q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-544q-40 0-68-28t-28-68v-1344q0-40 28-68t68-28h1088q40 0 68 28t28 68v328q21 13 36 28l408 408q28 28 48 76t20 88z"/></svg></button>'
copyIcon.onclick = (e) => {
copy(content)
if (showCopyNotification) {
this.notify('Saved to Clipboard!', {
body: 'Paste it wherever you want!',
silent: true
})
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('.code code'),
el => {
let syntax = CodeMirror.findModeByName(convertModeName(el.className))
if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text')
CodeMirror.requireMode(syntax.mode, () => {
const content = htmlTextHelper.decodeEntities(el.innerHTML)
const copyIcon = document.createElement('i')
copyIcon.innerHTML =
'<button class="clipboardButton"><svg width="13" height="13" viewBox="0 0 1792 1792" ><path d="M768 1664h896v-640h-416q-40 0-68-28t-28-68v-416h-384v1152zm256-1440v-64q0-13-9.5-22.5t-22.5-9.5h-704q-13 0-22.5 9.5t-9.5 22.5v64q0 13 9.5 22.5t22.5 9.5h704q13 0 22.5-9.5t9.5-22.5zm256 672h299l-299-299v299zm512 128v672q0 40-28 68t-68 28h-960q-40 0-68-28t-28-68v-160h-544q-40 0-68-28t-28-68v-1344q0-40 28-68t68-28h1088q40 0 68 28t28 68v328q21 13 36 28l408 408q28 28 48 76t20 88z"/></svg></button>'
copyIcon.onclick = e => {
copy(content)
if (showCopyNotification) {
this.notify('Saved to Clipboard!', {
body: 'Paste it wherever you want!',
silent: true
})
}
}
}
el.parentNode.appendChild(copyIcon)
el.innerHTML = ''
if (codeBlockTheme.indexOf('solarized') === 0) {
const [refThema, color] = codeBlockTheme.split(' ')
el.parentNode.className += ` cm-s-${refThema} cm-s-${color}`
} else {
el.parentNode.className += ` cm-s-${codeBlockTheme}`
}
CodeMirror.runMode(content, syntax.mime, el, {
tabSize: indentSize
el.parentNode.appendChild(copyIcon)
el.innerHTML = ''
if (codeBlockTheme.indexOf('solarized') === 0) {
const [refThema, color] = codeBlockTheme.split(' ')
el.parentNode.className += ` cm-s-${refThema} cm-s-${color}`
} else {
el.parentNode.className += ` cm-s-${codeBlockTheme}`
}
CodeMirror.runMode(content, syntax.mime, el, {
tabSize: indentSize
})
})
})
})
}
)
const opts = {}
// if (this.props.theme === 'dark') {
// opts['font-color'] = '#DDD'
@@ -463,37 +641,47 @@ export default class MarkdownPreview extends React.Component {
// opts['element-color'] = '#DDD'
// opts['fill'] = '#3A404C'
// }
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.flowchart'), (el) => {
Raphael.setWindow(this.getWindow())
try {
const diagram = flowchart.parse(htmlTextHelper.decodeEntities(el.innerHTML))
el.innerHTML = ''
diagram.drawSVG(el, opts)
_.forEach(el.querySelectorAll('a'), (el) => {
el.addEventListener('click', this.linkClickHandler)
})
} catch (e) {
console.error(e)
el.className = 'flowchart-error'
el.innerHTML = 'Flowchart parse error: ' + e.message
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('.flowchart'),
el => {
Raphael.setWindow(this.getWindow())
try {
const diagram = flowchart.parse(
htmlTextHelper.decodeEntities(el.innerHTML)
)
el.innerHTML = ''
diagram.drawSVG(el, opts)
_.forEach(el.querySelectorAll('a'), el => {
el.addEventListener('click', this.linkClickHandler)
})
} catch (e) {
console.error(e)
el.className = 'flowchart-error'
el.innerHTML = 'Flowchart parse error: ' + e.message
}
}
})
)
_.forEach(this.refs.root.contentWindow.document.querySelectorAll('.sequence'), (el) => {
Raphael.setWindow(this.getWindow())
try {
const diagram = SequenceDiagram.parse(htmlTextHelper.decodeEntities(el.innerHTML))
el.innerHTML = ''
diagram.drawSVG(el, {theme: 'simple'})
_.forEach(el.querySelectorAll('a'), (el) => {
el.addEventListener('click', this.linkClickHandler)
})
} catch (e) {
console.error(e)
el.className = 'sequence-error'
el.innerHTML = 'Sequence diagram parse error: ' + e.message
_.forEach(
this.refs.root.contentWindow.document.querySelectorAll('.sequence'),
el => {
Raphael.setWindow(this.getWindow())
try {
const diagram = SequenceDiagram.parse(
htmlTextHelper.decodeEntities(el.innerHTML)
)
el.innerHTML = ''
diagram.drawSVG(el, { theme: 'simple' })
_.forEach(el.querySelectorAll('a'), el => {
el.addEventListener('click', this.linkClickHandler)
})
} catch (e) {
console.error(e)
el.className = 'sequence-error'
el.innerHTML = 'Sequence diagram parse error: ' + e.message
}
}
})
)
}
focus () {
@@ -505,7 +693,9 @@ export default class MarkdownPreview extends React.Component {
}
scrollTo (targetRow) {
const blocks = this.getWindow().document.querySelectorAll('body>[data-line]')
const blocks = this.getWindow().document.querySelectorAll(
'body>[data-line]'
)
for (let index = 0; index < blocks.length; index++) {
let block = blocks[index]
@@ -525,7 +715,11 @@ export default class MarkdownPreview extends React.Component {
notify (title, options) {
if (global.process.platform === 'win32') {
options.icon = path.join('file://', global.__dirname, '../../resources/app.png')
options.icon = path.join(
'file://',
global.__dirname,
'../../resources/app.png'
)
}
return new window.Notification(title, options)
}
@@ -540,7 +734,9 @@ export default class MarkdownPreview extends React.Component {
const regexNoteInternalLink = /main.html#(.+)/
if (regexNoteInternalLink.test(linkHash)) {
const targetId = mdurl.encode(linkHash.match(regexNoteInternalLink)[1])
const targetElement = this.refs.root.contentWindow.document.getElementById(targetId)
const targetElement = this.refs.root.contentWindow.document.getElementById(
targetId
)
if (targetElement != null) {
this.getWindow().scrollTo(0, targetElement.offsetTop)
@@ -574,11 +770,15 @@ export default class MarkdownPreview extends React.Component {
render () {
const { className, style, tabIndex } = this.props
return (
<iframe className={className != null
? 'MarkdownPreview ' + className
: 'MarkdownPreview'
<iframe
className={
className != null ? 'MarkdownPreview ' + className : 'MarkdownPreview'
}
style={
this.state.isReady
? Object.assign(style, { opacity: '1' })
: Object.assign(style, { opacity: '0' })
}
style={style}
tabIndex={tabIndex}
ref='root'
/>