From 9b54272f8e748edfdb4f7a0af65c26b5f0850b5f Mon Sep 17 00:00:00 2001 From: Georges Indrianjafy Date: Sun, 4 Feb 2018 13:57:00 +0200 Subject: [PATCH 01/17] feat(code-editor): fetch title when pasting url --- browser/components/CodeEditor.js | 66 +++++++++++++++++++++++--------- 1 file changed, 48 insertions(+), 18 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 5d799935..2b6cfafc 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -226,26 +226,56 @@ export default class CodeEditor extends React.Component { } handlePaste (editor, e) { - const dataTransferItem = e.clipboardData.items[0] - if (!dataTransferItem.type.match('image')) return + const clipboardData = e.clipboardData + const dataTransferItem = clipboardData.items[0] + const pasteTxt = clipboardData.getData('text') + const isURL = (str) => { + const matcher = /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/ + return matcher.test(str) + } + if (dataTransferItem.type.match('image')) { + const blob = dataTransferItem.getAsFile() + const reader = new FileReader() + let base64data - const blob = dataTransferItem.getAsFile() - const reader = new FileReader() - let base64data + reader.readAsDataURL(blob) + reader.onloadend = () => { + base64data = reader.result.replace(/^data:image\/png;base64,/, '') + base64data += base64data.replace('+', ' ') + const binaryData = new Buffer(base64data, 'base64').toString('binary') + const imageName = Math.random().toString(36).slice(-16) + const storagePath = findStorage(this.props.storageKey).path + const imageDir = path.join(storagePath, 'images') + if (!fs.existsSync(imageDir)) fs.mkdirSync(imageDir) + const imagePath = path.join(imageDir, `${imageName}.png`) + fs.writeFile(imagePath, binaryData, 'binary') + const imageMd = `![${imageName}](${path.join('/:storage', `${imageName}.png`)})` + this.insertImageMd(imageMd) + } + } else if (this.props.fetchUrlTitle && isURL(pasteTxt)) { + e.preventDefault() + const taggedUrl = '[' + pasteTxt + ']' + editor.replaceSelection(taggedUrl) - reader.readAsDataURL(blob) - reader.onloadend = () => { - base64data = reader.result.replace(/^data:image\/png;base64,/, '') - base64data += base64data.replace('+', ' ') - const binaryData = new Buffer(base64data, 'base64').toString('binary') - const imageName = Math.random().toString(36).slice(-16) - const storagePath = findStorage(this.props.storageKey).path - const imageDir = path.join(storagePath, 'images') - if (!fs.existsSync(imageDir)) fs.mkdirSync(imageDir) - const imagePath = path.join(imageDir, `${imageName}.png`) - fs.writeFile(imagePath, binaryData, 'binary') - const imageMd = `![${imageName}](${path.join('/:storage', `${imageName}.png`)})` - this.insertImageMd(imageMd) + fetch(pasteTxt, { + method: 'get' + }).then((response) => { + return (response.text()) + }).then((response) => { + const parsedResponse = (new window.DOMParser()).parseFromString(response, 'text/html') + const value = editor.getValue() + const cursor = editor.getCursor() + const LinkWithTitle = `[${parsedResponse.title}](${pasteTxt})` + const newValue = value.replace(taggedUrl, LinkWithTitle) + editor.setValue(newValue) + editor.setCursor(cursor) + }).catch((e) => { + const value = editor.getValue() + const newValue = value.replace(taggedUrl, pasteTxt) + const cursor = editor.getCursor() + editor.setValue(newValue) + editor.setCursor(cursor) + }) } } From 56d6eb7077d1effc927a6a50c38cb7083f293431 Mon Sep 17 00:00:00 2001 From: Georges Indrianjafy Date: Sun, 4 Feb 2018 13:58:02 +0200 Subject: [PATCH 02/17] feat(config): add ablility to opt out of fetch url --- browser/components/MarkdownEditor.js | 1 + browser/components/MarkdownSplitEditor.js | 1 + browser/finder/NoteDetail.js | 1 + browser/main/Detail/SnippetNoteDetail.js | 1 + browser/main/lib/ConfigManager.js | 3 ++- browser/main/modals/PreferencesModal/UiTab.js | 14 +++++++++++++- 6 files changed, 19 insertions(+), 2 deletions(-) diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 1b44166c..62c4414a 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -244,6 +244,7 @@ class MarkdownEditor extends React.Component { indentSize={editorIndentSize} scrollPastEnd={config.editor.scrollPastEnd} storageKey={storageKey} + fetchUrlTitle={config.editor.fetchUrlTitle} onChange={(e) => this.handleChange(e)} onBlur={(e) => this.handleBlur(e)} /> diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index d0a3eb27..15498662 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -65,6 +65,7 @@ class MarkdownSplitEditor extends React.Component { indentType={config.editor.indentType} indentSize={editorIndentSize} scrollPastEnd={config.editor.scrollPastEnd} + fetchUrlTitle={config.editor.fetchUrlTitle} storageKey={storageKey} onChange={this.handleOnChange.bind(this)} /> diff --git a/browser/finder/NoteDetail.js b/browser/finder/NoteDetail.js index 3b9121d7..1325e87f 100644 --- a/browser/finder/NoteDetail.js +++ b/browser/finder/NoteDetail.js @@ -159,6 +159,7 @@ class NoteDetail extends React.Component { indentSize={editorIndentSize} keyMap={config.editor.keyMap} scrollPastEnd={config.editor.scrollPastEnd} + fetchUrlTitle={config.editor.fetchUrlTitle} readOnly ref={'code-' + index} /> diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index af8c178e..44c36492 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -566,6 +566,7 @@ class SnippetNoteDetail extends React.Component { indentSize={editorIndentSize} keyMap={config.editor.keyMap} scrollPastEnd={config.editor.scrollPastEnd} + fetchUrlTitle={config.editor.fetchUrlTitle} onChange={(e) => this.handleCodeChange(index)(e)} ref={'code-' + index} /> diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index 834646b4..4c0d410f 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -36,7 +36,8 @@ export const DEFAULT_CONFIG = { indentSize: '2', switchPreview: 'BLUR', // Available value: RIGHTCLICK, BLUR scrollPastEnd: false, - type: 'SPLIT' + type: 'SPLIT', + fetchUrlTitle: true }, preview: { fontSize: '14', diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index ed633c7d..d4afd2a6 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -75,7 +75,8 @@ class UiTab extends React.Component { indentSize: this.refs.editorIndentSize.value, switchPreview: this.refs.editorSwitchPreview.value, keyMap: this.refs.editorKeyMap.value, - scrollPastEnd: this.refs.scrollPastEnd.checked + scrollPastEnd: this.refs.scrollPastEnd.checked, + fetchUrlTitle: this.refs.editorFetchUrlTitle.checked, }, preview: { fontSize: this.refs.previewFontSize.value, @@ -315,6 +316,17 @@ class UiTab extends React.Component { +
+ +
+
Preview
From 10daf2599db79b3d5606126155336059075d7a9c Mon Sep 17 00:00:00 2001 From: Georges Indrianjafy Date: Sun, 4 Feb 2018 17:39:32 +0200 Subject: [PATCH 03/17] chore: fix lint isues --- browser/main/modals/PreferencesModal/UiTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index d4afd2a6..86f8fa2a 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -76,7 +76,7 @@ class UiTab extends React.Component { switchPreview: this.refs.editorSwitchPreview.value, keyMap: this.refs.editorKeyMap.value, scrollPastEnd: this.refs.scrollPastEnd.checked, - fetchUrlTitle: this.refs.editorFetchUrlTitle.checked, + fetchUrlTitle: this.refs.editorFetchUrlTitle.checked }, preview: { fontSize: this.refs.previewFontSize.value, From 3e2b876c591575f0bae54b6d45a2f66ca71e2a19 Mon Sep 17 00:00:00 2001 From: Georges Indrianjafy Date: Sun, 4 Feb 2018 17:49:23 +0200 Subject: [PATCH 04/17] fix(config): refrase --- browser/main/modals/PreferencesModal/UiTab.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index 86f8fa2a..dc161514 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -323,7 +323,7 @@ class UiTab extends React.Component { ref='editorFetchUrlTitle' type='checkbox' />  - Allow editor to fetch url titles on when pasting url + Bring in web page title when pasting URL on editor
From d9d46cda1cabf1d81fbe0e5e04fa4f8c29bbca83 Mon Sep 17 00:00:00 2001 From: Georges Indrianjafy Date: Mon, 5 Feb 2018 10:59:36 +0200 Subject: [PATCH 05/17] feat(editor): extract pasting url method --- browser/components/CodeEditor.js | 54 +++++++++++++++++--------------- 1 file changed, 29 insertions(+), 25 deletions(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 2b6cfafc..ccc70b8a 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -228,7 +228,7 @@ export default class CodeEditor extends React.Component { handlePaste (editor, e) { const clipboardData = e.clipboardData const dataTransferItem = clipboardData.items[0] - const pasteTxt = clipboardData.getData('text') + const pastedTxt = clipboardData.getData('text') const isURL = (str) => { const matcher = /^(?:\w+:)?\/\/([^\s\.]+\.\S{2}|localhost[\:?\d]*)\S*$/ return matcher.test(str) @@ -252,33 +252,37 @@ export default class CodeEditor extends React.Component { const imageMd = `![${imageName}](${path.join('/:storage', `${imageName}.png`)})` this.insertImageMd(imageMd) } - } else if (this.props.fetchUrlTitle && isURL(pasteTxt)) { - e.preventDefault() - const taggedUrl = '[' + pasteTxt + ']' - editor.replaceSelection(taggedUrl) - - fetch(pasteTxt, { - method: 'get' - }).then((response) => { - return (response.text()) - }).then((response) => { - const parsedResponse = (new window.DOMParser()).parseFromString(response, 'text/html') - const value = editor.getValue() - const cursor = editor.getCursor() - const LinkWithTitle = `[${parsedResponse.title}](${pasteTxt})` - const newValue = value.replace(taggedUrl, LinkWithTitle) - editor.setValue(newValue) - editor.setCursor(cursor) - }).catch((e) => { - const value = editor.getValue() - const newValue = value.replace(taggedUrl, pasteTxt) - const cursor = editor.getCursor() - editor.setValue(newValue) - editor.setCursor(cursor) - }) + } else if (this.props.fetchUrlTitle && isURL(pastedTxt)) { + this.handlePasteUrl(e, editor, pastedTxt) } } + handlePasteUrl(e, editor, pastedTxt){ + e.preventDefault() + const taggedUrl = '[' + pastedTxt + ']' + editor.replaceSelection(taggedUrl) + + fetch(pastedTxt, { + method: 'get' + }).then((response) => { + return (response.text()) + }).then((response) => { + const parsedResponse = (new window.DOMParser()).parseFromString(response, 'text/html') + const value = editor.getValue() + const cursor = editor.getCursor() + const LinkWithTitle = `[${parsedResponse.title}](${pastedTxt})` + const newValue = value.replace(taggedUrl, LinkWithTitle) + editor.setValue(newValue) + editor.setCursor(cursor) + }).catch((e) => { + const value = editor.getValue() + const newValue = value.replace(taggedUrl, pastedTxt) + const cursor = editor.getCursor() + editor.setValue(newValue) + editor.setCursor(cursor) + }) + } + render () { const { className, fontSize } = this.props let fontFamily = this.props.fontFamily From c42ac1df1b21e96ab977dd6a9526494ef6e44e2b Mon Sep 17 00:00:00 2001 From: Georges Indrianjafy Date: Mon, 5 Feb 2018 11:00:56 +0200 Subject: [PATCH 06/17] chore(editor): resolve lint issues --- browser/components/CodeEditor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index ccc70b8a..d50f3d8e 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -257,7 +257,7 @@ export default class CodeEditor extends React.Component { } } - handlePasteUrl(e, editor, pastedTxt){ + handlePasteUrl (e, editor, pastedTxt) { e.preventDefault() const taggedUrl = '[' + pastedTxt + ']' editor.replaceSelection(taggedUrl) From a676ebf46cbd474477f6a1f3c237248fa43bac65 Mon Sep 17 00:00:00 2001 From: Georges Indrianjafy Date: Tue, 6 Feb 2018 18:35:11 +0200 Subject: [PATCH 07/17] fix(editor): replace [] with <> --- browser/components/CodeEditor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 7e59ea65..adc51321 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -264,7 +264,7 @@ export default class CodeEditor extends React.Component { handlePasteUrl (e, editor, pastedTxt) { e.preventDefault() - const taggedUrl = '[' + pastedTxt + ']' + const taggedUrl = '<' + pastedTxt + '>' editor.replaceSelection(taggedUrl) fetch(pastedTxt, { From dc1b059a9da5a4998a46f82bb2a5dbb252620043 Mon Sep 17 00:00:00 2001 From: Georges Indrianjafy Date: Tue, 6 Feb 2018 19:12:25 +0200 Subject: [PATCH 08/17] clean up --- browser/components/CodeEditor.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index adc51321..75baffb3 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -264,7 +264,7 @@ export default class CodeEditor extends React.Component { handlePasteUrl (e, editor, pastedTxt) { e.preventDefault() - const taggedUrl = '<' + pastedTxt + '>' + const taggedUrl = `<${pastedTxt}>` editor.replaceSelection(taggedUrl) fetch(pastedTxt, { From c43589fe6af135c86f3a839a0c639cf6ba7c5ab6 Mon Sep 17 00:00:00 2001 From: Masahide Morio Date: Sun, 11 Feb 2018 02:28:03 +0900 Subject: [PATCH 09/17] Add file name and any first line number in code block (fixed) --- browser/components/markdown.styl | 8 ++++++++ browser/lib/markdown.js | 16 ++++++++++++---- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/browser/components/markdown.styl b/browser/components/markdown.styl index d1d306e9..b64af56c 100644 --- a/browser/components/markdown.styl +++ b/browser/components/markdown.styl @@ -220,6 +220,7 @@ pre background-color white &.CodeMirror height initial + flex-wrap wrap &>code flex 1 overflow-x auto @@ -229,6 +230,13 @@ pre padding 0 border none border-radius 0 + &>span.filename + width 100% + border-radius: 5px 0px 0px 0px + margin -8px 100% 8px -8px + padding 0px 6px + background-color #777; + color white &>span.lineNumber display none font-size 1em diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index c3510f89..e6ba46d6 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -9,10 +9,11 @@ import {lastFindInArray} from './utils' const katex = window.katex const config = ConfigManager.get() -function createGutter (str) { - const lc = (str.match(/\n/g) || []).length +function createGutter (str, fc) { + if(Number.isNaN(fc)) fc = 1 + const lc = (str.match(/\n/g) || []).length + fc - 1 const lines = [] - for (let i = 1; i <= lc; i++) { + for (let i = fc; i <= lc; i++) { lines.push('' + i + '') } return '' + lines.join('') + '' @@ -25,6 +26,12 @@ var md = markdownit({ xhtmlOut: true, breaks: true, highlight: function (str, lang) { + let delimiter = ":"; + let langinfo = lang.split(delimiter); + let lang = langinfo[0]; + let filename = langinfo[1] || ""; + let fc = parseInt(langinfo[2],10); + if (lang === 'flowchart') { return `
${str}
` } @@ -32,7 +39,8 @@ var md = markdownit({ return `
${str}
` } return '
' +
-      createGutter(str) +
+      '' + filename + '' +
+      createGutter(str, fc) +
       '' +
       str +
       '
' From 3194a7b1a2f108111a44da7c897065b3209d3b7d Mon Sep 17 00:00:00 2001 From: Masahide Morio Date: Mon, 19 Feb 2018 23:02:25 +0900 Subject: [PATCH 10/17] followed eslintrc --- browser/lib/markdown.js | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index e6ba46d6..003dd58d 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -10,7 +10,7 @@ const katex = window.katex const config = ConfigManager.get() function createGutter (str, fc) { - if(Number.isNaN(fc)) fc = 1 + if (Number.isNaN(fc)) fc = 1 const lc = (str.match(/\n/g) || []).length + fc - 1 const lines = [] for (let i = fc; i <= lc; i++) { @@ -26,22 +26,22 @@ var md = markdownit({ xhtmlOut: true, breaks: true, highlight: function (str, lang) { - let delimiter = ":"; - let langinfo = lang.split(delimiter); - let lang = langinfo[0]; - let filename = langinfo[1] || ""; - let fc = parseInt(langinfo[2],10); + const delimiter = ':' + const langInfo = lang.split(delimiter) + const langType = langInfo[0] + const fileName = langInfo[1] || '' + const fc = parseInt(langInfo[2], 10) - if (lang === 'flowchart') { + if (langType === 'flowchart') { return `
${str}
` } - if (lang === 'sequence') { + if (langType === 'sequence') { return `
${str}
` } return '
' +
-      '' + filename + '' +
+      '' + fileName + '' +
       createGutter(str, fc) +
-      '' +
+      '' +
       str +
       '
' } From c82eba05d19c522bafd965fdd5fd7d833be8aa8d Mon Sep 17 00:00:00 2001 From: Sander Steenhuis Date: Fri, 9 Feb 2018 03:19:04 +0100 Subject: [PATCH 11/17] Sync Split Editor scroll position --- browser/components/CodeEditor.js | 9 +++++ browser/components/MarkdownPreview.js | 9 +++++ browser/components/MarkdownSplitEditor.js | 44 +++++++++++++++++++++++ 3 files changed, 62 insertions(+) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 00dc1f6d..20ad3185 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -47,6 +47,7 @@ export default class CodeEditor extends React.Component { this.loadStyleHandler = (e) => { this.editor.refresh() } + this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, {leading: false, trailing: true}) } componentDidMount () { @@ -107,6 +108,7 @@ export default class CodeEditor extends React.Component { this.editor.on('blur', this.blurHandler) this.editor.on('change', this.changeHandler) this.editor.on('paste', this.pasteHandler) + this.editor.on('scroll', this.scrollHandler) const editorTheme = document.getElementById('editorTheme') editorTheme.addEventListener('load', this.loadStyleHandler) @@ -126,6 +128,7 @@ export default class CodeEditor extends React.Component { this.editor.off('blur', this.blurHandler) this.editor.off('change', this.changeHandler) this.editor.off('paste', this.pasteHandler) + this.editor.off('scroll', this.scrollHandler) const editorTheme = document.getElementById('editorTheme') editorTheme.removeEventListener('load', this.loadStyleHandler) } @@ -254,6 +257,12 @@ export default class CodeEditor extends React.Component { } } + handleScroll (e) { + if (this.props.onScroll) { + this.props.onScroll(e) + } + } + render () { const { className, fontSize } = this.props let fontFamily = this.props.fontFamily diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 1d0f7513..c5b0355d 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -121,6 +121,7 @@ export default class MarkdownPreview extends React.Component { 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.anchorClickHandler = (e) => this.handlePreviewAnchorClick(e) this.checkboxClickHandler = (e) => this.handleCheckboxClick(e) this.saveAsTextHandler = () => this.handleSaveAsText() @@ -151,6 +152,12 @@ export default class MarkdownPreview extends React.Component { this.props.onCheckboxClick(e) } + handleScroll (e) { + if (this.props.onScroll) { + this.props.onScroll(e) + } + } + handleContextMenu (e) { this.props.onContextMenu(e) } @@ -279,6 +286,7 @@ export default class MarkdownPreview extends React.Component { 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) @@ -292,6 +300,7 @@ export default class MarkdownPreview extends React.Component { 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) diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index 2cf8e322..deae403a 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -2,6 +2,7 @@ import React from 'react' import CodeEditor from 'browser/components/CodeEditor' import MarkdownPreview from 'browser/components/MarkdownPreview' import { findStorage } from 'browser/lib/findStorage' +import _ from 'lodash' import styles from './MarkdownSplitEditor.styl' import CSSModules from 'browser/lib/CSSModules' @@ -12,6 +13,7 @@ class MarkdownSplitEditor extends React.Component { this.value = props.value this.focus = () => this.refs.code.focus() this.reload = () => this.refs.code.reload() + this.userScroll = true } handleOnChange () { @@ -19,6 +21,46 @@ class MarkdownSplitEditor extends React.Component { this.props.onChange() } + handleScroll (e) { + const previewDoc = _.get(this, 'refs.preview.refs.root.contentWindow.document') + const codeDoc = _.get(this, 'refs.code.editor.doc') + let srcTop, srcHeight, targetTop, targetHeight + + if (this.userScroll) { + if (e.doc) { + srcTop = _.get(e, 'doc.scrollTop') + srcHeight = _.get(e, 'doc.height') + targetTop = _.get(previewDoc, 'body.scrollTop') + targetHeight = _.get(previewDoc, 'body.scrollHeight') + } else { + srcTop = _.get(previewDoc, 'body.scrollTop') + srcHeight = _.get(previewDoc, 'body.scrollHeight') + targetTop = _.get(codeDoc, 'scrollTop') + targetHeight = _.get(codeDoc, 'height') + } + + const factor = srcTop / srcHeight + const previewTarget = targetHeight * factor + const distance = previewTarget - targetTop + + this.userScroll = false + const s = 20 + let i = s + let f, t + const timer = setInterval(() => { + t = (s - i) / s + f = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t + if (e.doc) _.set(previewDoc, 'body.scrollTop', targetTop + f * distance) + else _.get(this, 'refs.code.editor').scrollTo(0, targetTop + f * distance) + if (!i) { + clearInterval(timer) + setTimeout(() => { this.userScroll = true }, 400) + } + i -= 1 + }, 1000 / 60) + } + } + handleCheckboxClick (e) { e.preventDefault() e.stopPropagation() @@ -68,6 +110,7 @@ class MarkdownSplitEditor extends React.Component { scrollPastEnd={config.editor.scrollPastEnd} storageKey={storageKey} onChange={this.handleOnChange.bind(this)} + onScroll={this.handleScroll.bind(this)} /> this.handleCheckboxClick(e)} + onScroll={this.handleScroll.bind(this)} showCopyNotification={config.ui.showCopyNotification} storagePath={storage.path} /> From 051ccad29aebd67e15485b87a74a2bb41b98e030 Mon Sep 17 00:00:00 2001 From: Sander Steenhuis Date: Tue, 20 Feb 2018 22:28:12 +0100 Subject: [PATCH 12/17] Rename variables --- browser/components/MarkdownSplitEditor.js | 31 +++++++++++++---------- 1 file changed, 17 insertions(+), 14 deletions(-) diff --git a/browser/components/MarkdownSplitEditor.js b/browser/components/MarkdownSplitEditor.js index deae403a..ea1a7b1e 100644 --- a/browser/components/MarkdownSplitEditor.js +++ b/browser/components/MarkdownSplitEditor.js @@ -39,25 +39,28 @@ class MarkdownSplitEditor extends React.Component { targetHeight = _.get(codeDoc, 'height') } - const factor = srcTop / srcHeight - const previewTarget = targetHeight * factor - const distance = previewTarget - targetTop + const distance = (targetHeight * srcTop / srcHeight) - targetTop + const framerate = 1000 / 60 + const frames = 20 + const refractory = frames * framerate this.userScroll = false - const s = 20 - let i = s - let f, t + + let frame = 0 + let scrollPos, time const timer = setInterval(() => { - t = (s - i) / s - f = t < 0.5 ? 2 * t * t : -1 + (4 - 2 * t) * t - if (e.doc) _.set(previewDoc, 'body.scrollTop', targetTop + f * distance) - else _.get(this, 'refs.code.editor').scrollTo(0, targetTop + f * distance) - if (!i) { + time = frame / frames + scrollPos = time < 0.5 + ? 2 * time * time // ease in + : -1 + (4 - 2 * time) * time // ease out + if (e.doc) _.set(previewDoc, 'body.scrollTop', targetTop + scrollPos * distance) + else _.get(this, 'refs.code.editor').scrollTo(0, targetTop + scrollPos * distance) + if (frame >= frames) { clearInterval(timer) - setTimeout(() => { this.userScroll = true }, 400) + setTimeout(() => { this.userScroll = true }, refractory) } - i -= 1 - }, 1000 / 60) + frame++ + }, framerate) } } From 3b524f6aba10bb5a49bec9d109ea15798c0bed16 Mon Sep 17 00:00:00 2001 From: Sander Steenhuis Date: Fri, 9 Feb 2018 20:03:07 +0100 Subject: [PATCH 13/17] Highlight global search matches on code editor --- browser/components/CodeEditor.js | 38 ++++++++++++++++++++++++++++++++ browser/main/TopBar/index.js | 12 +++++++++- 2 files changed, 49 insertions(+), 1 deletion(-) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 00dc1f6d..1e0a1ff5 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -7,6 +7,7 @@ import path from 'path' import copyImage from 'browser/main/lib/dataApi/copyImage' import { findStorage } from 'browser/lib/findStorage' import fs from 'fs' +import eventEmitter from 'browser/main/lib/eventEmitter' CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js' @@ -47,6 +48,39 @@ export default class CodeEditor extends React.Component { this.loadStyleHandler = (e) => { this.editor.refresh() } + this.searchHandler = (e, msg) => this.handleSearch(msg) + this.searchState = null + } + + handleSearch (msg) { + const cm = this.editor + const component = this + + if (component.searchState) cm.removeOverlay(component.searchState) + if (msg.length < 3) return + + cm.operation(function () { + component.searchState = makeOverlay(msg, 'searching') + cm.addOverlay(component.searchState) + + function makeOverlay (query, style) { + query = new RegExp(query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), 'gi') + return { + token: function (stream) { + query.lastIndex = stream.pos + var match = query.exec(stream.string) + if (match && match.index === stream.pos) { + stream.pos += match[0].length || 1 + return style + } else if (match) { + stream.pos = match.index + } else { + stream.skipToEnd() + } + } + } + } + }) } componentDidMount () { @@ -107,6 +141,9 @@ export default class CodeEditor extends React.Component { this.editor.on('blur', this.blurHandler) this.editor.on('change', this.changeHandler) this.editor.on('paste', this.pasteHandler) + eventEmitter.on('top:search', this.searchHandler) + + eventEmitter.emit('code:init') const editorTheme = document.getElementById('editorTheme') editorTheme.addEventListener('load', this.loadStyleHandler) @@ -126,6 +163,7 @@ export default class CodeEditor extends React.Component { this.editor.off('blur', this.blurHandler) this.editor.off('change', this.changeHandler) this.editor.off('paste', this.pasteHandler) + eventEmitter.off('top:search', this.searchHandler) const editorTheme = document.getElementById('editorTheme') editorTheme.removeEventListener('load', this.loadStyleHandler) } diff --git a/browser/main/TopBar/index.js b/browser/main/TopBar/index.js index 11d31abd..6b4a118e 100644 --- a/browser/main/TopBar/index.js +++ b/browser/main/TopBar/index.js @@ -22,14 +22,18 @@ class TopBar extends React.Component { this.focusSearchHandler = () => { this.handleOnSearchFocus() } + + this.codeInitHandler = this.handleCodeInit.bind(this) } componentDidMount () { ee.on('top:focus-search', this.focusSearchHandler) + ee.on('code:init', this.codeInitHandler) } componentWillUnmount () { ee.off('top:focus-search', this.focusSearchHandler) + ee.off('code:init', this.codeInitHandler) } handleKeyDown (e) { @@ -73,14 +77,16 @@ class TopBar extends React.Component { handleSearchChange (e) { const { router } = this.context + const keyword = this.refs.searchInput.value if (this.state.isAlphabet || this.state.isConfirmTranslation) { router.push('/searched') } else { e.preventDefault() } this.setState({ - search: this.refs.searchInput.value + search: keyword }) + ee.emit('top:search', keyword) } handleSearchFocus (e) { @@ -115,6 +121,10 @@ class TopBar extends React.Component { } } + handleCodeInit () { + ee.emit('top:search', this.refs.searchInput.value) + } + render () { const { config, style, location } = this.props return ( From 90473203011ce21e11ab9f275164bffeea728376 Mon Sep 17 00:00:00 2001 From: Sander Steenhuis Date: Wed, 21 Feb 2018 19:17:38 +0100 Subject: [PATCH 14/17] Update CodeEditor.js - Forgot curly brace Forgot curly brace when merging master --- browser/components/CodeEditor.js | 1 + 1 file changed, 1 insertion(+) diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 27f983b4..3d27c1c3 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -269,6 +269,7 @@ export default class CodeEditor extends React.Component { if (this.props.onScroll) { this.props.onScroll(e) } + } handlePasteUrl (e, editor, pastedTxt) { e.preventDefault() From deab34abd6bdf28ee427b79fc0466d0a3533f189 Mon Sep 17 00:00:00 2001 From: Junyoung Choi Date: Thu, 22 Feb 2018 09:39:56 +0900 Subject: [PATCH 15/17] Fix memory lick --- browser/main/Detail/MarkdownNoteDetail.js | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js index bb76b8f3..7694f1e7 100755 --- a/browser/main/Detail/MarkdownNoteDetail.js +++ b/browser/main/Detail/MarkdownNoteDetail.js @@ -68,11 +68,8 @@ class MarkdownNoteDetail extends React.Component { } componentWillUnmount () { - if (this.saveQueue != null) this.saveNow() - } - - componentDidUnmount () { ee.off('topbar:togglelockbutton', this.toggleLockButton) + if (this.saveQueue != null) this.saveNow() } handleUpdateTag () { From c3586372e0e054d1cdfcbba188f42b91a5e62cde Mon Sep 17 00:00:00 2001 From: Junyoung Choi Date: Thu, 22 Feb 2018 10:49:59 +0900 Subject: [PATCH 16/17] Rename variables --- browser/lib/markdown.js | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index 003dd58d..ce511c0b 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -9,11 +9,11 @@ import {lastFindInArray} from './utils' const katex = window.katex const config = ConfigManager.get() -function createGutter (str, fc) { - if (Number.isNaN(fc)) fc = 1 - const lc = (str.match(/\n/g) || []).length + fc - 1 +function createGutter (str, firstLineNumber) { + if (Number.isNaN(firstLineNumber)) firstLineNumber = 1 + const lastLineNumber = (str.match(/\n/g) || []).length + firstLineNumber - 1 const lines = [] - for (let i = fc; i <= lc; i++) { + for (let i = firstLineNumber; i <= lastLineNumber; i++) { lines.push('' + i + '') } return '' + lines.join('') + '' From e4fbdb35a3226e3b9f23c9a743cbeb80463849d7 Mon Sep 17 00:00:00 2001 From: Junyoung Choi Date: Thu, 22 Feb 2018 10:54:12 +0900 Subject: [PATCH 17/17] Rename a variable name again --- browser/lib/markdown.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index ce511c0b..d0801a1b 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -30,7 +30,7 @@ var md = markdownit({ const langInfo = lang.split(delimiter) const langType = langInfo[0] const fileName = langInfo[1] || '' - const fc = parseInt(langInfo[2], 10) + const firstLineNumber = parseInt(langInfo[2], 10) if (langType === 'flowchart') { return `
${str}
` @@ -40,7 +40,7 @@ var md = markdownit({ } return '
' +
       '' + fileName + '' +
-      createGutter(str, fc) +
+      createGutter(str, firstLineNumber) +
       '' +
       str +
       '
'