diff --git a/.eslintrc b/.eslintrc index be8cb903..67b6c8fe 100644 --- a/.eslintrc +++ b/.eslintrc @@ -1,6 +1,6 @@ { - "extends": ["standard", "standard-jsx", "plugin:react/recommended"], - "plugins": ["react"], + "extends": ["standard", "standard-jsx", "plugin:react/recommended", "prettier"], + "plugins": ["react", "prettier"], "rules": { "no-useless-escape": 0, "prefer-const": ["warn", { @@ -13,7 +13,8 @@ "react/no-string-refs": 0, "react/no-find-dom-node": "warn", "react/no-render-return-value": "warn", - "react/no-deprecated": "warn" + "react/no-deprecated": "warn", + "prettier/prettier": ["error"] }, "globals": { "FileReader": true, diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 00000000..515c6cd5 --- /dev/null +++ b/.prettierrc @@ -0,0 +1,5 @@ +{ + "singleQuote": true, + "semi": false, + "jsxSingleQuote": true +} \ No newline at end of file diff --git a/browser/components/CodeEditor.js b/browser/components/CodeEditor.js index 7080b1fc..a5bb009a 100644 --- a/browser/components/CodeEditor.js +++ b/browser/components/CodeEditor.js @@ -6,11 +6,7 @@ import hljs from 'highlight.js' import 'codemirror-mode-elixir' import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' import convertModeName from 'browser/lib/convertModeName' -import { - options, - TableEditor, - Alignment -} from '@susisu/mte-kernel' +import { options, TableEditor, Alignment } from '@susisu/mte-kernel' import TextEditorInterface from 'browser/lib/TextEditorInterface' import eventEmitter from 'browser/main/lib/eventEmitter' import iconv from 'iconv-lite' @@ -20,11 +16,15 @@ import styles from '../components/CodeEditor.styl' const { ipcRenderer, remote, clipboard } = require('electron') import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' const spellcheck = require('browser/lib/spellcheck') -const buildEditorContextMenu = require('browser/lib/contextMenuBuilder').buildEditorContextMenu +const buildEditorContextMenu = require('browser/lib/contextMenuBuilder') + .buildEditorContextMenu import { createTurndownService } from '../lib/turndown' -import {languageMaps} from '../lib/CMLanguageList' +import { languageMaps } from '../lib/CMLanguageList' import snippetManager from '../lib/SnippetManager' -import {generateInEditor, tocExistsInEditor} from 'browser/lib/markdown-toc-generator' +import { + generateInEditor, + tocExistsInEditor +} from 'browser/lib/markdown-toc-generator' import markdownlint from 'markdownlint' import Jsonlint from 'jsonlint-mod' import { DEFAULT_CONFIG } from '../main/lib/ConfigManager' @@ -33,28 +33,38 @@ import prettier from 'prettier' CodeMirror.modeURL = '../node_modules/codemirror/mode/%N/%N.js' const buildCMRulers = (rulers, enableRulers) => - (enableRulers ? rulers.map(ruler => ({ - column: ruler - })) : []) + enableRulers + ? rulers.map(ruler => ({ + column: ruler + })) + : [] -function translateHotkey (hotkey) { - return hotkey.replace(/\s*\+\s*/g, '-').replace(/Command/g, 'Cmd').replace(/Control/g, 'Ctrl') +function translateHotkey(hotkey) { + return hotkey + .replace(/\s*\+\s*/g, '-') + .replace(/Command/g, 'Cmd') + .replace(/Control/g, 'Ctrl') } export default class CodeEditor extends React.Component { - constructor (props) { + constructor(props) { super(props) this.scrollHandler = _.debounce(this.handleScroll.bind(this), 100, { leading: false, trailing: true }) - this.changeHandler = (editor, changeObject) => this.handleChange(editor, changeObject) - this.highlightHandler = (editor, changeObject) => this.handleHighlight(editor, changeObject) + this.changeHandler = (editor, changeObject) => + this.handleChange(editor, changeObject) + this.highlightHandler = (editor, changeObject) => + this.handleHighlight(editor, changeObject) this.focusHandler = () => { ipcRenderer.send('editor:focused', true) } - const debouncedDeletionOfAttachments = _.debounce(attachmentManagement.deleteAttachmentsNotPresentInNote, 30000) + const debouncedDeletionOfAttachments = _.debounce( + attachmentManagement.deleteAttachmentsNotPresentInNote, + 30000 + ) this.blurHandler = (editor, e) => { ipcRenderer.send('editor:focused', false) if (e == null) return null @@ -66,12 +76,13 @@ export default class CodeEditor extends React.Component { el = el.parentNode } this.props.onBlur != null && this.props.onBlur(e) - const { - storageKey, - noteKey - } = this.props + const { storageKey, noteKey } = this.props if (this.props.deleteUnusedAttachments === true) { - debouncedDeletionOfAttachments(this.editor.getValue(), storageKey, noteKey) + debouncedDeletionOfAttachments( + this.editor.getValue(), + storageKey, + noteKey + ) } } this.pasteHandler = (editor, e) => { @@ -91,7 +102,7 @@ export default class CodeEditor extends React.Component { this.formatTable = () => this.handleFormatTable() if (props.switchPreview !== 'RIGHTCLICK') { - this.contextMenuHandler = function (editor, event) { + this.contextMenuHandler = function(editor, event) { const menu = buildEditorContextMenu(editor, event) if (menu != null) { setTimeout(() => menu.popup(remote.getCurrentWindow()), 30) @@ -104,24 +115,24 @@ export default class CodeEditor extends React.Component { this.turndownService = createTurndownService() } - handleSearch (msg) { + handleSearch(msg) { const cm = this.editor const component = this if (component.searchState) cm.removeOverlay(component.searchState) if (msg.length < 1) return - cm.operation(function () { + cm.operation(function() { component.searchState = makeOverlay(msg, 'searching') cm.addOverlay(component.searchState) - function makeOverlay (query, style) { + function makeOverlay(query, style) { query = new RegExp( query.replace(/[\-\[\]\/\{\}\(\)\*\+\?\.\\\^\$\|]/g, '\\$&'), 'gi' ) return { - token: function (stream) { + token: function(stream) { query.lastIndex = stream.pos var match = query.exec(stream.string) if (match && match.index === stream.pos) { @@ -138,25 +149,27 @@ export default class CodeEditor extends React.Component { }) } - handleFormatTable () { - this.tableEditor.formatAll(options({ - textWidthOptions: {} - })) + handleFormatTable() { + this.tableEditor.formatAll( + options({ + textWidthOptions: {} + }) + ) } - handleEditorActivity () { + handleEditorActivity() { if (!this.textEditorInterface.transaction) { this.updateTableEditorState() } } - updateDefaultKeyMap () { + updateDefaultKeyMap() { const { hotkey } = this.props const self = this const expandSnippet = snippetManager.expandSnippet this.defaultKeyMap = CodeMirror.normalizeKeyMap({ - Tab: function (cm) { + Tab: function(cm) { const cursor = cm.getCursor() const line = cm.getLine(cursor.line) const cursorPosition = cursor.ch @@ -198,17 +211,17 @@ export default class CodeEditor extends React.Component { } } }, - 'Cmd-Left': function (cm) { + 'Cmd-Left': function(cm) { cm.execCommand('goLineLeft') }, - 'Cmd-T': function (cm) { + 'Cmd-T': function(cm) { // Do nothing }, - [translateHotkey(hotkey.insertDate)]: function (cm) { + [translateHotkey(hotkey.insertDate)]: function(cm) { const dateNow = new Date() cm.replaceSelection(dateNow.toLocaleDateString()) }, - [translateHotkey(hotkey.insertDateTime)]: function (cm) { + [translateHotkey(hotkey.insertDateTime)]: function(cm) { const dateNow = new Date() cm.replaceSelection(dateNow.toLocaleString()) }, @@ -231,7 +244,10 @@ export default class CodeEditor extends React.Component { currentConfig.cursorOffset = cm.doc.indexFromPos(cursorPos) // Prettify contents of editor - const formattedTextDetails = prettier.formatWithCursor(cm.doc.getValue(), currentConfig) + const formattedTextDetails = prettier.formatWithCursor( + cm.doc.getValue(), + currentConfig + ) const formattedText = formattedTextDetails.formatted const formattedCursorPos = formattedTextDetails.cursorOffset @@ -246,7 +262,8 @@ export default class CodeEditor extends React.Component { const appendLineBreak = /\n$/.test(selection) const sorted = _.split(selection.trim(), '\n').sort() - const sortedString = _.join(sorted, '\n') + (appendLineBreak ? '\n' : '') + const sortedString = + _.join(sorted, '\n') + (appendLineBreak ? '\n' : '') cm.doc.replaceSelection(sortedString) }, @@ -256,7 +273,7 @@ export default class CodeEditor extends React.Component { }) } - updateTableEditorState () { + updateTableEditorState() { const active = this.tableEditor.cursorIsInTable(this.tableEditorOptions) if (active) { if (this.extraKeysMode !== 'editor') { @@ -272,8 +289,8 @@ export default class CodeEditor extends React.Component { } } - componentDidMount () { - const { rulers, enableRulers, enableMarkdownLint } = this.props + componentDidMount() { + const { rulers, enableRulers, enableMarkdownLint, RTL } = this.props eventEmitter.on('line:jump', this.scrollToLineHandeler) snippetManager.init() @@ -294,9 +311,15 @@ export default class CodeEditor extends React.Component { scrollPastEnd: this.props.scrollPastEnd, inputStyle: 'textarea', dragDrop: false, + direction: RTL ? 'rtl' : 'ltr', + rtlMoveVisually: RTL, foldGutter: true, lint: enableMarkdownLint ? this.getCodeEditorLintConfig() : false, - gutters: ['CodeMirror-linenumbers', 'CodeMirror-foldgutter', 'CodeMirror-lint-markers'], + gutters: [ + 'CodeMirror-linenumbers', + 'CodeMirror-foldgutter', + 'CodeMirror-lint-markers' + ], autoCloseBrackets: { pairs: this.props.matchingPairs, triples: this.props.matchingTriples, @@ -307,7 +330,9 @@ export default class CodeEditor extends React.Component { prettierConfig: this.props.prettierConfig }) - document.querySelector('.CodeMirror-lint-markers').style.display = enableMarkdownLint ? 'inline-block' : 'none' + document.querySelector( + '.CodeMirror-lint-markers' + ).style.display = enableMarkdownLint ? 'inline-block' : 'none' if (!this.props.mode && this.props.value && this.props.autoDetect) { this.autoDetectLanguage(this.props.value) @@ -340,7 +365,7 @@ export default class CodeEditor extends React.Component { this.textEditorInterface = new TextEditorInterface(this.editor) this.tableEditor = new TableEditor(this.textEditorInterface) if (this.props.spellCheck) { - this.editor.addPanel(this.createSpellCheckPanel(), {position: 'bottom'}) + this.editor.addPanel(this.createSpellCheckPanel(), { position: 'bottom' }) } eventEmitter.on('code:format-table', this.formatTable) @@ -350,13 +375,13 @@ export default class CodeEditor extends React.Component { }) this.editorKeyMap = CodeMirror.normalizeKeyMap({ - 'Tab': () => { + Tab: () => { this.tableEditor.nextCell(this.tableEditorOptions) }, 'Shift-Tab': () => { this.tableEditor.previousCell(this.tableEditorOptions) }, - 'Enter': () => { + Enter: () => { this.tableEditor.nextRow(this.tableEditorOptions) }, 'Ctrl-Enter': () => { @@ -475,7 +500,7 @@ export default class CodeEditor extends React.Component { this.initialHighlighting() } - getWordBeforeCursor (line, lineNumber, cursorPosition) { + getWordBeforeCursor(line, lineNumber, cursorPosition) { let wordBeforeCursor = '' const originCursorPosition = cursorPosition const emptyChars = /\t|\s|\r|\n|\$/ @@ -512,11 +537,11 @@ export default class CodeEditor extends React.Component { } } - quitEditor () { + quitEditor() { document.querySelector('textarea').blur() } - componentWillUnmount () { + componentWillUnmount() { this.editor.off('focus', this.focusHandler) this.editor.off('blur', this.blurHandler) this.editor.off('change', this.changeHandler) @@ -531,7 +556,7 @@ export default class CodeEditor extends React.Component { eventEmitter.off('code:format-table', this.formatTable) } - componentDidUpdate (prevProps, prevState) { + componentDidUpdate(prevProps, prevState) { let needRefresh = false const { rulers, @@ -555,13 +580,22 @@ export default class CodeEditor extends React.Component { if (prevProps.keyMap !== this.props.keyMap) { needRefresh = true } - if (prevProps.enableMarkdownLint !== enableMarkdownLint || prevProps.customMarkdownLintConfig !== customMarkdownLintConfig) { + if (prevProps.RTL !== this.props.RTL) { + this.editor.setOption('direction', this.props.RTL ? 'rtl' : 'ltr') + this.editor.setOption('rtlMoveVisually', this.props.RTL) + } + if ( + prevProps.enableMarkdownLint !== enableMarkdownLint || + prevProps.customMarkdownLintConfig !== customMarkdownLintConfig + ) { if (!enableMarkdownLint) { - this.editor.setOption('lint', {default: false}) - document.querySelector('.CodeMirror-lint-markers').style.display = 'none' + this.editor.setOption('lint', { default: false }) + document.querySelector('.CodeMirror-lint-markers').style.display = + 'none' } else { this.editor.setOption('lint', this.getCodeEditorLintConfig()) - document.querySelector('.CodeMirror-lint-markers').style.display = 'inline-block' + document.querySelector('.CodeMirror-lint-markers').style.display = + 'inline-block' } needRefresh = true } @@ -593,9 +627,11 @@ export default class CodeEditor extends React.Component { this.editor.setOption('scrollPastEnd', this.props.scrollPastEnd) } - if (prevProps.matchingPairs !== this.props.matchingPairs || + if ( + prevProps.matchingPairs !== this.props.matchingPairs || prevProps.matchingTriples !== this.props.matchingTriples || - prevProps.explodingPairs !== this.props.explodingPairs) { + prevProps.explodingPairs !== this.props.explodingPairs + ) { const bracketObject = { pairs: this.props.matchingPairs, triples: this.props.matchingTriples, @@ -640,11 +676,18 @@ export default class CodeEditor extends React.Component { const elem = document.getElementById('editor-bottom-panel') elem.parentNode.removeChild(elem) } else { - this.editor.addPanel(this.createSpellCheckPanel(), {position: 'bottom'}) + this.editor.addPanel(this.createSpellCheckPanel(), { + position: 'bottom' + }) } } - if (prevProps.deleteUnusedAttachments !== this.props.deleteUnusedAttachments) { - this.editor.setOption('deleteUnusedAttachments', this.props.deleteUnusedAttachments) + if ( + prevProps.deleteUnusedAttachments !== this.props.deleteUnusedAttachments + ) { + this.editor.setOption( + 'deleteUnusedAttachments', + this.props.deleteUnusedAttachments + ) } if (needRefresh) { @@ -652,17 +695,19 @@ export default class CodeEditor extends React.Component { } } - getCodeEditorLintConfig () { + getCodeEditorLintConfig() { const { mode } = this.props const checkMarkdownNoteIsOpen = mode === 'Boost Flavored Markdown' - return checkMarkdownNoteIsOpen ? { - getAnnotations: this.validatorOfMarkdown, - async: true - } : false + return checkMarkdownNoteIsOpen + ? { + getAnnotations: this.validatorOfMarkdown, + async: true + } + : false } - validatorOfMarkdown (text, updateLinting) { + validatorOfMarkdown(text, updateLinting) { const { customMarkdownLintConfig } = this.props let lintConfigJson try { @@ -687,7 +732,7 @@ export default class CodeEditor extends React.Component { let ruleNames = '' item.ruleNames.map((ruleName, index) => { ruleNames += ruleName - ruleNames += (index === item.ruleNames.length - 1) ? ': ' : '/' + ruleNames += index === item.ruleNames.length - 1 ? ': ' : '/' }) const lineNumber = item.lineNumber - 1 foundIssues.push({ @@ -702,7 +747,7 @@ export default class CodeEditor extends React.Component { }) } - setMode (mode) { + setMode(mode) { let syntax = CodeMirror.findModeByName(convertModeName(mode || 'text')) if (syntax == null) syntax = CodeMirror.findModeByName('Plain Text') @@ -710,7 +755,7 @@ export default class CodeEditor extends React.Component { CodeMirror.autoLoadMode(this.editor, syntax.mode) } - handleChange (editor, changeObject) { + handleChange(editor, changeObject) { spellcheck.handleChange(editor, changeObject) // The current note contains an toc. We'll check for changes on headlines. @@ -720,7 +765,11 @@ export default class CodeEditor extends React.Component { // Check if one of the changed lines contains a headline for (let line = 0; line < changeObject.text.length; line++) { - if (this.linePossibleContainsHeadline(editor.getLine(changeObject.from.line + line))) { + if ( + this.linePossibleContainsHeadline( + editor.getLine(changeObject.from.line + line) + ) + ) { requireTocUpdate = true break } @@ -749,13 +798,13 @@ export default class CodeEditor extends React.Component { } } - linePossibleContainsHeadline (currentLine) { + linePossibleContainsHeadline(currentLine) { // We can't check if the line start with # because when some write text before // the # we also need to update the toc return currentLine.includes('# ') } - incrementLines (start, linesAdded, linesRemoved, editor) { + incrementLines(start, linesAdded, linesRemoved, editor) { const highlightedLines = editor.options.linesHighlighted const totalHighlightedLines = highlightedLines.length @@ -776,7 +825,7 @@ export default class CodeEditor extends React.Component { highlightedLines.splice(highlightedLines.indexOf(lineNumber), 1) // Lines that need to be relocated - if (lineNumber >= (start + linesRemoved)) { + if (lineNumber >= start + linesRemoved) { newLines.push(lineNumber + offset) } } @@ -790,22 +839,30 @@ export default class CodeEditor extends React.Component { } } - handleHighlight (editor, changeObject) { + handleHighlight(editor, changeObject) { const lines = editor.options.linesHighlighted if (!lines.includes(changeObject)) { lines.push(changeObject) - editor.addLineClass(changeObject, 'text', 'CodeMirror-activeline-background') + editor.addLineClass( + changeObject, + 'text', + 'CodeMirror-activeline-background' + ) } else { lines.splice(lines.indexOf(changeObject), 1) - editor.removeLineClass(changeObject, 'text', 'CodeMirror-activeline-background') + editor.removeLineClass( + changeObject, + 'text', + 'CodeMirror-activeline-background' + ) } if (this.props.onChange) { this.props.onChange(editor) } } - updateHighlight (editor, changeObject) { + updateHighlight(editor, changeObject) { const linesAdded = changeObject.text.length - 1 const linesRemoved = changeObject.removed.length - 1 @@ -836,28 +893,28 @@ export default class CodeEditor extends React.Component { this.incrementLines(start, linesAdded, linesRemoved, editor) } - moveCursorTo (row, col) {} + moveCursorTo(row, col) {} - scrollToLine (event, num) { + scrollToLine(event, num) { const cursor = { line: num, ch: 1 } this.editor.setCursor(cursor) - const top = this.editor.charCoords({line: num, ch: 0}, 'local').top + const top = this.editor.charCoords({ line: num, ch: 0 }, 'local').top const middleHeight = this.editor.getScrollerElement().offsetHeight / 2 this.editor.scrollTo(null, top - middleHeight - 5) } - focus () { + focus() { this.editor.focus() } - blur () { + blur() { this.editor.blur() } - reload () { + reload() { // Change event shouldn't be fired when switch note this.editor.off('change', this.changeHandler) this.value = this.props.value @@ -868,7 +925,7 @@ export default class CodeEditor extends React.Component { this.editor.refresh() } - setValue (value) { + setValue(value) { const cursor = this.editor.getCursor() this.editor.setValue(value) this.editor.setCursor(cursor) @@ -879,18 +936,19 @@ export default class CodeEditor extends React.Component { * @param {Number} lineNumber * @param {String} content */ - setLineContent (lineNumber, content) { + setLineContent(lineNumber, content) { const prevContent = this.editor.getLine(lineNumber) const prevContentLength = prevContent ? prevContent.length : 0 - this.editor.replaceRange(content, { line: lineNumber, ch: 0 }, { line: lineNumber, ch: prevContentLength }) + this.editor.replaceRange( + content, + { line: lineNumber, ch: 0 }, + { line: lineNumber, ch: prevContentLength } + ) } - handleDropImage (dropEvent) { + handleDropImage(dropEvent) { dropEvent.preventDefault() - const { - storageKey, - noteKey - } = this.props + const { storageKey, noteKey } = this.props attachmentManagement.handleAttachmentDrop( this, storageKey, @@ -899,37 +957,44 @@ export default class CodeEditor extends React.Component { ) } - insertAttachmentMd (imageMd) { + insertAttachmentMd(imageMd) { this.editor.replaceSelection(imageMd) } - autoDetectLanguage (content) { + autoDetectLanguage(content) { const res = hljs.highlightAuto(content, Object.keys(languageMaps)) this.setMode(languageMaps[res.language]) } - handlePaste (editor, forceSmartPaste) { + handlePaste(editor, forceSmartPaste) { const { storageKey, noteKey, fetchUrlTitle, enableSmartPaste } = this.props - const isURL = str => /(?:^\w+:|^)\/\/(?:[^\s\.]+\.\S{2}|localhost[\:?\d]*)/.test(str) + const isURL = str => + /(?:^\w+:|^)\/\/(?:[^\s\.]+\.\S{2}|localhost[\:?\d]*)/.test(str) const isInLinkTag = editor => { const startCursor = editor.getCursor('start') - const prevChar = editor.getRange({ - line: startCursor.line, - ch: startCursor.ch - 2 - }, { - line: startCursor.line, - ch: startCursor.ch - }) + const prevChar = editor.getRange( + { + line: startCursor.line, + ch: startCursor.ch - 2 + }, + { + line: startCursor.line, + ch: startCursor.ch + } + ) const endCursor = editor.getCursor('end') - const nextChar = editor.getRange({ - line: endCursor.line, - ch: endCursor.ch - }, { - line: endCursor.line, - ch: endCursor.ch + 1 - }) + const nextChar = editor.getRange( + { + line: endCursor.line, + ch: endCursor.ch + }, + { + line: endCursor.line, + ch: endCursor.ch + 1 + } + ) return prevChar === '](' && nextChar === ')' } @@ -941,7 +1006,7 @@ export default class CodeEditor extends React.Component { return true } - let line = line = cursor.line - 1 + let line = (line = cursor.line - 1) while (line >= 0) { token = editor.getTokenAt({ ch: 3, @@ -973,7 +1038,11 @@ export default class CodeEditor extends React.Component { if (isInFencedCodeBlock(editor)) { this.handlePasteText(editor, pastedTxt) - } else if (fetchUrlTitle && isMarkdownTitleURL(pastedTxt) && !isInLinkTag(editor)) { + } else if ( + fetchUrlTitle && + isMarkdownTitleURL(pastedTxt) && + !isInLinkTag(editor) + ) { this.handlePasteUrl(editor, pastedTxt) } else if (fetchUrlTitle && isURL(pastedTxt) && !isInLinkTag(editor)) { this.handlePasteUrl(editor, pastedTxt) @@ -1009,13 +1078,13 @@ export default class CodeEditor extends React.Component { } } - handleScroll (e) { + handleScroll(e) { if (this.props.onScroll) { this.props.onScroll(e) } } - handlePasteUrl (editor, pastedTxt) { + handlePasteUrl(editor, pastedTxt) { let taggedUrl = `<${pastedTxt}>` let urlToFetch = pastedTxt let titleMark = '' @@ -1065,16 +1134,16 @@ export default class CodeEditor extends React.Component { }) } - handlePasteHtml (editor, pastedHtml) { + handlePasteHtml(editor, pastedHtml) { const markdown = this.turndownService.turndown(pastedHtml) editor.replaceSelection(markdown) } - handlePasteText (editor, pastedTxt) { + handlePasteText(editor, pastedTxt) { editor.replaceSelection(pastedTxt) } - mapNormalResponse (response, pastedTxt) { + mapNormalResponse(response, pastedTxt) { return this.decodeResponse(response).then(body => { return new Promise((resolve, reject) => { try { @@ -1082,10 +1151,12 @@ export default class CodeEditor extends React.Component { body, 'text/html' ) - const escapePipe = (str) => { + const escapePipe = str => { return str.replace('|', '\\|') } - const linkWithTitle = `[${escapePipe(parsedBody.title)}](${pastedTxt})` + const linkWithTitle = `[${escapePipe( + parsedBody.title + )}](${pastedTxt})` resolve(linkWithTitle) } catch (e) { reject(e) @@ -1094,7 +1165,7 @@ export default class CodeEditor extends React.Component { }) } - initialHighlighting () { + initialHighlighting() { if (this.editor.options.linesHighlighted == null) { return } @@ -1108,16 +1179,20 @@ export default class CodeEditor extends React.Component { // make sure that we skip the invalid lines althrough this case should not be happened. continue } - this.editor.addLineClass(lineNumber, 'text', 'CodeMirror-activeline-background') + this.editor.addLineClass( + lineNumber, + 'text', + 'CodeMirror-activeline-background' + ) } } - restartHighlighting () { + restartHighlighting() { this.editor.options.linesHighlighted = this.props.linesHighlighted this.initialHighlighting() } - mapImageResponse (response, pastedTxt) { + mapImageResponse(response, pastedTxt) { return new Promise((resolve, reject) => { try { const url = response.url @@ -1130,7 +1205,7 @@ export default class CodeEditor extends React.Component { }) } - decodeResponse (response) { + decodeResponse(response) { const headers = response.headers const _charset = headers.has('content-type') ? this.extractContentTypeCharset(headers.get('content-type')) @@ -1138,10 +1213,10 @@ export default class CodeEditor extends React.Component { return response.arrayBuffer().then(buff => { return new Promise((resolve, reject) => { try { - const charset = _charset !== undefined && - iconv.encodingExists(_charset) - ? _charset - : 'utf-8' + const charset = + _charset !== undefined && iconv.encodingExists(_charset) + ? _charset + : 'utf-8' resolve(iconv.decode(Buffer.from(buff), charset).toString()) } catch (e) { reject(e) @@ -1150,50 +1225,49 @@ export default class CodeEditor extends React.Component { }) } - extractContentTypeCharset (contentType) { + extractContentTypeCharset(contentType) { return contentType .split(';') .filter(str => { - return str.trim().toLowerCase().startsWith('charset') + return str + .trim() + .toLowerCase() + .startsWith('charset') }) .map(str => { return str.replace(/['"]/g, '').split('=')[1] })[0] } - render () { - const { - className, - fontSize - } = this.props + render() { + const { className, fontSize } = this.props const fontFamily = normalizeEditorFontFamily(this.props.fontFamily) const width = this.props.width - return (< - div className={ - className == null ? 'CodeEditor' : `CodeEditor ${className}` - } - ref='root' - tabIndex='-1' - style={{ - fontFamily, - fontSize: fontSize, - width: width - }} - onDrop={ - e => this.handleDropImage(e) - } + return ( +
this.handleDropImage(e)} /> ) } - createSpellCheckPanel () { + createSpellCheckPanel() { const panel = document.createElement('div') panel.className = 'panel bottom' panel.id = 'editor-bottom-panel' const dropdown = document.createElement('select') dropdown.title = 'Spellcheck' dropdown.className = styles['spellcheck-select'] - dropdown.addEventListener('change', (e) => spellcheck.setLanguage(this.editor, dropdown.value)) + dropdown.addEventListener('change', e => + spellcheck.setLanguage(this.editor, dropdown.value) + ) const options = spellcheck.getAvailableDictionaries() for (const op of options) { const option = document.createElement('option') @@ -1219,7 +1293,8 @@ CodeEditor.propTypes = { spellCheck: PropTypes.bool, enableMarkdownLint: PropTypes.bool, customMarkdownLintConfig: PropTypes.string, - deleteUnusedAttachments: PropTypes.bool + deleteUnusedAttachments: PropTypes.bool, + RTL: PropTypes.bool } CodeEditor.defaultProps = { @@ -1235,5 +1310,6 @@ CodeEditor.defaultProps = { enableMarkdownLint: DEFAULT_CONFIG.editor.enableMarkdownLint, customMarkdownLintConfig: DEFAULT_CONFIG.editor.customMarkdownLintConfig, prettierConfig: DEFAULT_CONFIG.editor.prettierConfig, - deleteUnusedAttachments: DEFAULT_CONFIG.editor.deleteUnusedAttachments + deleteUnusedAttachments: DEFAULT_CONFIG.editor.deleteUnusedAttachments, + RTL: false } diff --git a/browser/components/ColorPicker.js b/browser/components/ColorPicker.js index 9e0199c2..4d4e80e4 100644 --- a/browser/components/ColorPicker.js +++ b/browser/components/ColorPicker.js @@ -7,7 +7,7 @@ import styles from './ColorPicker.styl' const componentHeight = 330 class ColorPicker extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -18,21 +18,21 @@ class ColorPicker extends React.Component { this.handleConfirm = this.handleConfirm.bind(this) } - componentWillReceiveProps (nextProps) { + componentWillReceiveProps(nextProps) { this.onColorChange(nextProps.color) } - onColorChange (color) { + onColorChange(color) { this.setState({ color }) } - handleConfirm () { + handleConfirm() { this.props.onConfirm(this.state.color) } - render () { + render() { const { onReset, onCancel, targetRect } = this.props const { color } = this.state @@ -44,13 +44,22 @@ class ColorPicker extends React.Component { } return ( -
+
- - - + + +
) diff --git a/browser/components/MarkdownEditor.js b/browser/components/MarkdownEditor.js index 5c2ddbdb..a8b88891 100644 --- a/browser/components/MarkdownEditor.js +++ b/browser/components/MarkdownEditor.js @@ -10,7 +10,7 @@ import ConfigManager from 'browser/main/lib/ConfigManager' import attachmentManagement from 'browser/main/lib/dataApi/attachmentManagement' class MarkdownEditor extends React.Component { - constructor (props) { + constructor(props) { super(props) // char codes for ctrl + w @@ -20,7 +20,10 @@ class MarkdownEditor extends React.Component { this.supportMdSelectionBold = [16, 17, 186] this.state = { - status: props.config.editor.switchPreview === 'RIGHTCLICK' ? props.config.editor.delfaultStatus : 'CODE', + status: + props.config.editor.switchPreview === 'RIGHTCLICK' + ? props.config.editor.delfaultStatus + : 'CODE', renderValue: props.value, keyPressed: new Set(), isLocked: props.isLocked @@ -29,133 +32,153 @@ class MarkdownEditor extends React.Component { this.lockEditorCode = () => this.handleLockEditor() } - componentDidMount () { + componentDidMount() { this.value = this.refs.code.value eventEmitter.on('editor:lock', this.lockEditorCode) eventEmitter.on('editor:focus', this.focusEditor.bind(this)) } - componentDidUpdate () { + componentDidUpdate() { this.value = this.refs.code.value } - componentWillReceiveProps (props) { + componentWillReceiveProps(props) { if (props.value !== this.props.value) { this.queueRendering(props.value) } } - componentWillUnmount () { + componentWillUnmount() { this.cancelQueue() eventEmitter.off('editor:lock', this.lockEditorCode) eventEmitter.off('editor:focus', this.focusEditor.bind(this)) } - focusEditor () { - this.setState({ - status: 'CODE' - }, () => { - this.refs.code.focus() - }) + focusEditor() { + this.setState( + { + status: 'CODE' + }, + () => { + this.refs.code.focus() + } + ) } - queueRendering (value) { + queueRendering(value) { clearTimeout(this.renderTimer) this.renderTimer = setTimeout(() => { this.renderPreview(value) }, 500) } - cancelQueue () { + cancelQueue() { clearTimeout(this.renderTimer) } - renderPreview (value) { + renderPreview(value) { this.setState({ renderValue: value }) } - setValue (value) { + setValue(value) { this.refs.code.setValue(value) } - handleChange (e) { + handleChange(e) { this.value = this.refs.code.value this.props.onChange(e) } - handleContextMenu (e) { + handleContextMenu(e) { if (this.state.isLocked) return const { config } = this.props if (config.editor.switchPreview === 'RIGHTCLICK') { const newStatus = this.state.status === 'PREVIEW' ? 'CODE' : 'PREVIEW' - this.setState({ - status: newStatus - }, () => { - if (newStatus === 'CODE') { - this.refs.code.focus() - } else { - this.refs.preview.focus() - } - eventEmitter.emit('topbar:togglelockbutton', this.state.status) + this.setState( + { + status: newStatus + }, + () => { + if (newStatus === 'CODE') { + this.refs.code.focus() + } else { + this.refs.preview.focus() + } + eventEmitter.emit('topbar:togglelockbutton', this.state.status) - const newConfig = Object.assign({}, config) - newConfig.editor.delfaultStatus = newStatus - ConfigManager.set(newConfig) - }) + const newConfig = Object.assign({}, config) + newConfig.editor.delfaultStatus = newStatus + ConfigManager.set(newConfig) + } + ) } } - handleBlur (e) { + handleBlur(e) { if (this.state.isLocked) return this.setState({ keyPressed: new Set() }) const { config } = this.props - if (config.editor.switchPreview === 'BLUR' || - (config.editor.switchPreview === 'DBL_CLICK' && this.state.status === 'CODE') + if ( + config.editor.switchPreview === 'BLUR' || + (config.editor.switchPreview === 'DBL_CLICK' && + this.state.status === 'CODE') ) { const cursorPosition = this.refs.code.editor.getCursor() - this.setState({ - status: 'PREVIEW' - }, () => { - this.refs.preview.focus() - this.refs.preview.scrollToRow(cursorPosition.line) - }) + this.setState( + { + status: 'PREVIEW' + }, + () => { + this.refs.preview.focus() + this.refs.preview.scrollToRow(cursorPosition.line) + } + ) eventEmitter.emit('topbar:togglelockbutton', this.state.status) } } - handleDoubleClick (e) { + handleDoubleClick(e) { if (this.state.isLocked) return - this.setState({keyPressed: new Set()}) + this.setState({ keyPressed: new Set() }) const { config } = this.props if (config.editor.switchPreview === 'DBL_CLICK') { - this.setState({ - status: 'CODE' - }, () => { - this.refs.code.focus() - eventEmitter.emit('topbar:togglelockbutton', this.state.status) - }) + this.setState( + { + status: 'CODE' + }, + () => { + this.refs.code.focus() + eventEmitter.emit('topbar:togglelockbutton', this.state.status) + } + ) } } - handlePreviewMouseDown (e) { + handlePreviewMouseDown(e) { this.previewMouseDownedAt = new Date() } - handlePreviewMouseUp (e) { + handlePreviewMouseUp(e) { const { config } = this.props - if (config.editor.switchPreview === 'BLUR' && new Date() - this.previewMouseDownedAt < 200) { - this.setState({ - status: 'CODE' - }, () => { - this.refs.code.focus() - }) + if ( + config.editor.switchPreview === 'BLUR' && + new Date() - this.previewMouseDownedAt < 200 + ) { + this.setState( + { + status: 'CODE' + }, + () => { + this.refs.code.focus() + } + ) eventEmitter.emit('topbar:togglelockbutton', this.state.status) } } - handleCheckboxClick (e) { + handleCheckboxClick(e) { e.preventDefault() e.stopPropagation() const idMatch = /checkbox-([0-9]+)/ @@ -164,9 +187,9 @@ class MarkdownEditor extends React.Component { const checkReplace = /\[x]/i const uncheckReplace = /\[ ]/ if (idMatch.test(e.target.getAttribute('id'))) { - const lineIndex = parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1 - const lines = this.refs.code.value - .split('\n') + const lineIndex = + parseInt(e.target.getAttribute('id').match(idMatch)[1], 10) - 1 + const lines = this.refs.code.value.split('\n') const targetLine = lines[lineIndex] let newLine = targetLine @@ -181,45 +204,56 @@ class MarkdownEditor extends React.Component { } } - focus () { + focus() { if (this.state.status === 'PREVIEW') { - this.setState({ - status: 'CODE' - }, () => { - this.refs.code.focus() - }) + this.setState( + { + status: 'CODE' + }, + () => { + this.refs.code.focus() + } + ) } else { this.refs.code.focus() } eventEmitter.emit('topbar:togglelockbutton', this.state.status) } - reload () { + reload() { this.refs.code.reload() this.cancelQueue() this.renderPreview(this.props.value) } - handleKeyDown (e) { + handleKeyDown(e) { const { config } = this.props if (this.state.status !== 'CODE') return false const keyPressed = this.state.keyPressed keyPressed.add(e.keyCode) this.setState({ keyPressed }) - const isNoteHandlerKey = (el) => { return keyPressed.has(el) } + const isNoteHandlerKey = el => { + return keyPressed.has(el) + } // These conditions are for ctrl-e and ctrl-w - if (keyPressed.size === this.escapeFromEditor.length && - !this.state.isLocked && this.state.status === 'CODE' && - this.escapeFromEditor.every(isNoteHandlerKey)) { + if ( + keyPressed.size === this.escapeFromEditor.length && + !this.state.isLocked && + this.state.status === 'CODE' && + this.escapeFromEditor.every(isNoteHandlerKey) + ) { this.handleContextMenu() if (config.editor.switchPreview === 'BLUR') document.activeElement.blur() } - if (keyPressed.size === this.supportMdSelectionBold.length && this.supportMdSelectionBold.every(isNoteHandlerKey)) { + if ( + keyPressed.size === this.supportMdSelectionBold.length && + this.supportMdSelectionBold.every(isNoteHandlerKey) + ) { this.addMdAroundWord('**') } } - addMdAroundWord (mdElement) { + addMdAroundWord(mdElement) { if (this.refs.code.editor.getSelection()) { return this.addMdAroundSelection(mdElement) } @@ -227,47 +261,63 @@ class MarkdownEditor extends React.Component { const word = this.refs.code.editor.findWordAt(currentCaret) const cmDoc = this.refs.code.editor.getDoc() cmDoc.replaceRange(mdElement, word.anchor) - cmDoc.replaceRange(mdElement, { line: word.head.line, ch: word.head.ch + mdElement.length }) - } - - addMdAroundSelection (mdElement) { - this.refs.code.editor.replaceSelection(`${mdElement}${this.refs.code.editor.getSelection()}${mdElement}`) - } - - handleDropImage (dropEvent) { - dropEvent.preventDefault() - const { storageKey, noteKey } = this.props - - this.setState({ - status: 'CODE' - }, () => { - this.refs.code.focus() - - this.refs.code.editor.execCommand('goDocEnd') - this.refs.code.editor.execCommand('goLineEnd') - this.refs.code.editor.execCommand('newlineAndIndent') - - attachmentManagement.handleAttachmentDrop( - this.refs.code, - storageKey, - noteKey, - dropEvent - ) + cmDoc.replaceRange(mdElement, { + line: word.head.line, + ch: word.head.ch + mdElement.length }) } - handleKeyUp (e) { + addMdAroundSelection(mdElement) { + this.refs.code.editor.replaceSelection( + `${mdElement}${this.refs.code.editor.getSelection()}${mdElement}` + ) + } + + handleDropImage(dropEvent) { + dropEvent.preventDefault() + const { storageKey, noteKey } = this.props + + this.setState( + { + status: 'CODE' + }, + () => { + this.refs.code.focus() + + this.refs.code.editor.execCommand('goDocEnd') + this.refs.code.editor.execCommand('goLineEnd') + this.refs.code.editor.execCommand('newlineAndIndent') + + attachmentManagement.handleAttachmentDrop( + this.refs.code, + storageKey, + noteKey, + dropEvent + ) + } + ) + } + + handleKeyUp(e) { const keyPressed = this.state.keyPressed keyPressed.delete(e.keyCode) this.setState({ keyPressed }) } - handleLockEditor () { + handleLockEditor() { this.setState({ isLocked: !this.state.isLocked }) } - render () { - const {className, value, config, storageKey, noteKey, linesHighlighted} = this.props + render() { + const { + className, + value, + config, + storageKey, + noteKey, + linesHighlighted, + RTL + } = this.props let editorFontSize = parseInt(config.editor.fontSize, 10) if (!(editorFontSize > 0 && editorFontSize < 101)) editorFontSize = 14 @@ -275,23 +325,24 @@ class MarkdownEditor extends React.Component { if (!(editorFontSize > 0 && editorFontSize < 132)) editorIndentSize = 4 const previewStyle = {} - if (this.props.ignorePreviewPointerEvents) previewStyle.pointerEvents = 'none' + if (this.props.ignorePreviewPointerEvents) + previewStyle.pointerEvents = 'none' const storage = findStorage(storageKey) return ( -
this.handleContextMenu(e)} + onContextMenu={e => this.handleContextMenu(e)} tabIndex='-1' - onKeyDown={(e) => this.handleKeyDown(e)} - onKeyUp={(e) => this.handleKeyUp(e)} + onKeyDown={e => this.handleKeyDown(e)} + onKeyUp={e => this.handleKeyUp(e)} > - this.handleChange(e)} - onBlur={(e) => this.handleBlur(e)} + onChange={e => this.handleChange(e)} + onBlur={e => this.handleBlur(e)} spellCheck={config.editor.spellcheck} enableSmartPaste={config.editor.enableSmartPaste} hotkey={config.hotkey} @@ -325,10 +376,11 @@ class MarkdownEditor extends React.Component { customMarkdownLintConfig={config.editor.customMarkdownLintConfig} prettierConfig={config.editor.prettierConfig} deleteUnusedAttachments={config.editor.deleteUnusedAttachments} + RTL={RTL} /> - this.handleContextMenu(e)} - onDoubleClick={(e) => this.handleDoubleClick(e)} + onContextMenu={e => this.handleContextMenu(e)} + onDoubleClick={e => this.handleDoubleClick(e)} tabIndex='0' value={this.state.renderValue} - onMouseUp={(e) => this.handlePreviewMouseUp(e)} - onMouseDown={(e) => this.handlePreviewMouseDown(e)} - onCheckboxClick={(e) => this.handleCheckboxClick(e)} + onMouseUp={e => this.handlePreviewMouseUp(e)} + onMouseDown={e => this.handlePreviewMouseDown(e)} + onCheckboxClick={e => this.handleCheckboxClick(e)} showCopyNotification={config.ui.showCopyNotification} storagePath={storage.path} noteKey={noteKey} customCSS={config.preview.customCSS} allowCustomCSS={config.preview.allowCustomCSS} lineThroughCheckbox={config.preview.lineThroughCheckbox} - onDrop={(e) => this.handleDropImage(e)} + onDrop={e => this.handleDropImage(e)} + RTL={RTL} />
) diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index 7c88f562..4306b0cf 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -11,6 +11,7 @@ import mermaidRender from './render/MermaidRender' import SequenceDiagram from '@rokt33r/js-sequence-diagrams' import Chart from 'chart.js' import eventEmitter from 'browser/main/lib/eventEmitter' +import config from 'browser/main/lib/ConfigManager' import htmlTextHelper from 'browser/lib/htmlTextHelper' import convertModeName from 'browser/lib/convertModeName' import copy from 'copy-to-clipboard' @@ -21,10 +22,13 @@ import yaml from 'js-yaml' import { render } from 'react-dom' import Carousel from 'react-image-carousel' import ConfigManager from '../main/lib/ConfigManager' +import uiThemes from 'browser/lib/ui-themes' +import i18n from 'browser/lib/i18n' const { remote, shell } = require('electron') const attachmentManagement = require('../main/lib/dataApi/attachmentManagement') -const buildMarkdownPreviewContextMenu = require('browser/lib/contextMenuBuilder').buildMarkdownPreviewContextMenu +const buildMarkdownPreviewContextMenu = require('browser/lib/contextMenuBuilder') + .buildMarkdownPreviewContextMenu const { app } = remote const path = require('path') @@ -54,7 +58,7 @@ const CSS_FILES = [ * @param {String} [opts.customCSS] Will be added to bottom, only if `opts.allowCustomCSS` is truthy * @returns {String} */ -function buildStyle (opts) { +function buildStyle(opts) { const { fontFamily, fontSize, @@ -63,7 +67,8 @@ function buildStyle (opts) { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL } = opts return ` @font-face { @@ -100,11 +105,17 @@ ${markdownStyle} body { font-family: '${fontFamily.join("','")}'; font-size: ${fontSize}px; - ${scrollPastEnd ? ` + + ${ + scrollPastEnd + ? ` padding-bottom: 90vh; box-sizing: border-box; ` - : ''} + : '' + } + ${RTL ? 'direction: rtl;' : ''} + ${RTL ? 'text-align: right;' : ''} } @media print { body { @@ -114,6 +125,8 @@ body { code { font-family: '${codeBlockFontFamily.join("','")}'; background-color: rgba(0,0,0,0.04); + text-align: left; + direction: ltr; } .lineNumber { ${lineNumber && 'display: block !important;'} @@ -144,14 +157,22 @@ h1, h2 { border: none; } +h3 { + margin: 1em 0 0.8em; +} + +h4, h5, h6 { + margin: 1.1em 0 0.5em; +} + h1 { - padding-bottom: 4px; + padding: 0.2em 0 0.2em; margin: 1em 0 8px; } h2 { - padding-bottom: 0.2em; - margin: 1em 0 0.37em; + padding: 0.2em 0 0.2em; + margin: 1em 0 0.7em; } body p { @@ -174,10 +195,12 @@ ${allowCustomCSS ? customCSS : ''} const scrollBarStyle = ` ::-webkit-scrollbar { + ${config.get().ui.showScrollBar ? '' : 'display: none;'} width: 12px; } ::-webkit-scrollbar-thumb { + ${config.get().ui.showScrollBar ? '' : 'display: none;'} background-color: rgba(0, 0, 0, 0.15); } @@ -187,10 +210,12 @@ const scrollBarStyle = ` ` const scrollBarDarkStyle = ` ::-webkit-scrollbar { + ${config.get().ui.showScrollBar ? '' : 'display: none;'} width: 12px; } ::-webkit-scrollbar-thumb { + ${config.get().ui.showScrollBar ? '' : 'display: none;'} background-color: rgba(0, 0, 0, 0.3); } @@ -217,7 +242,7 @@ const defaultCodeBlockFontFamily = [ // return the line number of the line that used to generate the specified element // return -1 if the line is not found -function getSourceLineNumberByElement (element) { +function getSourceLineNumberByElement(element) { let isHasLineNumber = element.dataset.line !== undefined let parent = element while (!isHasLineNumber && parent.parentElement !== null) { @@ -228,7 +253,7 @@ function getSourceLineNumberByElement (element) { } export default class MarkdownPreview extends React.Component { - constructor (props) { + constructor(props) { super(props) this.contextMenuHandler = e => this.handleContextMenu(e) @@ -245,13 +270,14 @@ export default class MarkdownPreview extends React.Component { this.saveAsHtmlHandler = () => this.handleSaveAsHtml() this.saveAsPdfHandler = () => this.handleSaveAsPdf() this.printHandler = () => this.handlePrint() + this.resizeHandler = _.throttle(this.handleResize.bind(this), 100) this.linkClickHandler = this.handleLinkClick.bind(this) this.initMarkdown = this.initMarkdown.bind(this) this.initMarkdown() } - initMarkdown () { + initMarkdown() { const { smartQuotes, sanitize, breaks } = this.props this.markdown = new Markdown({ typographer: smartQuotes, @@ -260,17 +286,17 @@ export default class MarkdownPreview extends React.Component { }) } - handleCheckboxClick (e) { + handleCheckboxClick(e) { this.props.onCheckboxClick(e) } - handleScroll (e) { + handleScroll(e) { if (this.props.onScroll) { this.props.onScroll(e) } } - handleContextMenu (event) { + handleContextMenu(event) { const menu = buildMarkdownPreviewContextMenu(this, event) const switchPreview = ConfigManager.get().editor.switchPreview if (menu != null && switchPreview !== 'RIGHTCLICK') { @@ -280,17 +306,21 @@ export default class MarkdownPreview extends React.Component { } } - handleDoubleClick (e) { + handleDoubleClick(e) { if (this.props.onDoubleClick != null) this.props.onDoubleClick(e) } - handleMouseDown (e) { + handleMouseDown(e) { const config = ConfigManager.get() const clickElement = e.target const targetTag = clickElement.tagName // The direct parent HTML of where was clicked ie "BODY" or "DIV" const lineNumber = getSourceLineNumberByElement(clickElement) // Line location of element clicked. - if (config.editor.switchPreview === 'RIGHTCLICK' && e.buttons === 2 && config.editor.type === 'SPLIT') { + if ( + config.editor.switchPreview === 'RIGHTCLICK' && + e.buttons === 2 && + config.editor.type === 'SPLIT' + ) { eventEmitter.emit('topbar:togglemodebutton', 'CODE') } if (e.ctrlKey) { @@ -306,10 +336,11 @@ export default class MarkdownPreview extends React.Component { } } - if (this.props.onMouseDown != null && targetTag === 'BODY') this.props.onMouseDown(e) + if (this.props.onMouseDown != null && targetTag === 'BODY') + this.props.onMouseDown(e) } - handleMouseUp (e) { + handleMouseUp(e) { if (!this.props.onMouseUp) return if (e.target != null && e.target.tagName === 'A') { return null @@ -317,15 +348,15 @@ export default class MarkdownPreview extends React.Component { if (this.props.onMouseUp != null) this.props.onMouseUp(e) } - handleSaveAsText () { + handleSaveAsText() { this.exportAsDocument('txt') } - handleSaveAsMd () { + handleSaveAsMd() { this.exportAsDocument('md') } - htmlContentFormatter (noteContent, exportTasks, targetDir) { + htmlContentFormatter(noteContent, exportTasks, targetDir) { const { fontFamily, fontSize, @@ -335,7 +366,8 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL } = this.getStyleParams() const inlineStyles = buildStyle({ @@ -346,13 +378,11 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL }) - let body = this.markdown.render(noteContent) - body = attachmentManagement.fixLocalURLS( - body, - this.props.storagePath - ) + let body = this.refs.root.contentWindow.document.body.innerHTML + body = attachmentManagement.fixLocalURLS(body, this.props.storagePath) const files = [this.getCodeThemeLink(codeBlockTheme), ...CSS_FILES] files.forEach(file => { if (global.process.platform === 'win32') { @@ -368,7 +398,7 @@ export default class MarkdownPreview extends React.Component { let styles = '' files.forEach(file => { - styles += `` + styles += `` }) return ` @@ -383,14 +413,24 @@ export default class MarkdownPreview extends React.Component { ` } - handleSaveAsHtml () { - this.exportAsDocument('html', (noteContent, exportTasks, targetDir) => Promise.resolve(this.htmlContentFormatter(noteContent, exportTasks, targetDir))) + handleSaveAsHtml() { + this.exportAsDocument('html', (noteContent, exportTasks, targetDir) => + Promise.resolve( + this.htmlContentFormatter(noteContent, exportTasks, targetDir) + ) + ) } - handleSaveAsPdf () { + handleSaveAsPdf() { this.exportAsDocument('pdf', (noteContent, exportTasks, targetDir) => { - const printout = new remote.BrowserWindow({show: false, webPreferences: {webSecurity: false, javascript: false}}) - printout.loadURL('data:text/html;charset=UTF-8,' + this.htmlContentFormatter(noteContent, exportTasks, targetDir)) + const printout = new remote.BrowserWindow({ + show: false, + webPreferences: { webSecurity: false, javascript: false } + }) + printout.loadURL( + 'data:text/html;charset=UTF-8,' + + this.htmlContentFormatter(noteContent, exportTasks, targetDir) + ) return new Promise((resolve, reject) => { printout.webContents.on('did-finish-load', () => { printout.webContents.printToPDF({}, (err, data) => { @@ -403,11 +443,11 @@ export default class MarkdownPreview extends React.Component { }) } - handlePrint () { + handlePrint() { this.refs.root.contentWindow.print() } - exportAsDocument (fileType, contentFormatter) { + exportAsDocument(fileType, contentFormatter) { const options = { filters: [{ name: 'Documents', extensions: [fileType] }], properties: ['openFile', 'createDirectory'] @@ -423,7 +463,8 @@ export default class MarkdownPreview extends React.Component { .then(res => { dialog.showMessageBox(remote.getCurrentWindow(), { type: 'info', - message: `Exported to ${filename}` + message: `Exported to ${filename}`, + buttons: [i18n.__('Ok')] }) }) .catch(err => { @@ -437,7 +478,7 @@ export default class MarkdownPreview extends React.Component { }) } - fixDecodedURI (node) { + fixDecodedURI(node) { if ( node && node.children.length === 1 && @@ -454,17 +495,18 @@ export default class MarkdownPreview extends React.Component { * @param {string[]} splitWithCodeTag Array of HTML strings separated by three ``` * @returns {string} HTML in which special characters between three ``` have been converted */ - escapeHtmlCharactersInCodeTag (splitWithCodeTag) { + escapeHtmlCharactersInCodeTag(splitWithCodeTag) { for (let index = 0; index < splitWithCodeTag.length; index++) { - const codeTagRequired = (splitWithCodeTag[index] !== '\`\`\`' && index < splitWithCodeTag.length - 1) + const codeTagRequired = + splitWithCodeTag[index] !== '```' && index < splitWithCodeTag.length - 1 if (codeTagRequired) { - splitWithCodeTag.splice((index + 1), 0, '\`\`\`') + splitWithCodeTag.splice(index + 1, 0, '```') } } let inCodeTag = false let result = '' for (let content of splitWithCodeTag) { - if (content === '\`\`\`') { + if (content === '```') { inCodeTag = !inCodeTag } else if (inCodeTag) { content = escapeHtmlCharacters(content) @@ -474,21 +516,15 @@ export default class MarkdownPreview extends React.Component { return result } - getScrollBarStyle () { + getScrollBarStyle() { const { theme } = this.props - switch (theme) { - case 'dark': - case 'solarized-dark': - case 'monokai': - case 'dracula': - return scrollBarDarkStyle - default: - return scrollBarStyle - } + return uiThemes.some(item => item.name === theme && item.isDark) + ? scrollBarDarkStyle + : scrollBarStyle } - componentDidMount () { + componentDidMount() { const { onDrop } = this.props this.refs.root.setAttribute('sandbox', 'allow-scripts') @@ -538,6 +574,7 @@ export default class MarkdownPreview extends React.Component { 'scroll', this.scrollHandler ) + this.refs.root.contentWindow.addEventListener('resize', this.resizeHandler) eventEmitter.on('export:save-text', this.saveAsTextHandler) eventEmitter.on('export:save-md', this.saveAsMdHandler) eventEmitter.on('export:save-html', this.saveAsHtmlHandler) @@ -545,7 +582,7 @@ export default class MarkdownPreview extends React.Component { eventEmitter.on('print', this.printHandler) } - componentWillUnmount () { + componentWillUnmount() { const { onDrop } = this.props this.refs.root.contentWindow.document.body.removeEventListener( @@ -576,6 +613,10 @@ export default class MarkdownPreview extends React.Component { 'scroll', this.scrollHandler ) + this.refs.root.contentWindow.removeEventListener( + 'resize', + this.resizeHandler + ) eventEmitter.off('export:save-text', this.saveAsTextHandler) eventEmitter.off('export:save-md', this.saveAsMdHandler) eventEmitter.off('export:save-html', this.saveAsHtmlHandler) @@ -583,7 +624,7 @@ export default class MarkdownPreview extends React.Component { eventEmitter.off('print', this.printHandler) } - componentDidUpdate (prevProps) { + componentDidUpdate(prevProps) { // actual rewriteIframe function should be called only once let needsRewriteIframe = false if (prevProps.value !== this.props.value) needsRewriteIframe = true @@ -608,7 +649,8 @@ 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 || + prevProps.RTL !== this.props.RTL ) { this.applyStyle() needsRewriteIframe = true @@ -624,7 +666,7 @@ export default class MarkdownPreview extends React.Component { } } - getStyleParams () { + getStyleParams() { const { fontSize, lineNumber, @@ -632,22 +674,24 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL } = 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 = + _.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 return { fontFamily, @@ -658,11 +702,12 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL } } - applyStyle () { + applyStyle() { const { fontFamily, fontSize, @@ -672,7 +717,8 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL } = this.getStyleParams() this.getWindow().document.getElementById( @@ -686,11 +732,12 @@ export default class MarkdownPreview extends React.Component { scrollPastEnd, theme, allowCustomCSS, - customCSS + customCSS, + RTL }) } - getCodeThemeLink (name) { + getCodeThemeLink(name) { const theme = consts.THEMES.find(theme => theme.name === name) return theme != null @@ -698,7 +745,7 @@ export default class MarkdownPreview extends React.Component { : `${appPath}/node_modules/codemirror/theme/elegant.css` } - rewriteIframe () { + rewriteIframe() { _.forEach( this.refs.root.contentWindow.document.querySelectorAll( 'input[type="checkbox"]' @@ -756,7 +803,9 @@ export default class MarkdownPreview extends React.Component { codeBlockTheme = consts.THEMES.find(theme => theme.name === codeBlockTheme) - const codeBlockThemeClassName = codeBlockTheme ? codeBlockTheme.className : 'cm-s-default' + const codeBlockThemeClassName = codeBlockTheme + ? codeBlockTheme.className + : 'cm-s-default' _.forEach( this.refs.root.contentWindow.document.querySelectorAll('.code code'), @@ -842,7 +891,10 @@ export default class MarkdownPreview extends React.Component { el => { try { const format = el.attributes.getNamedItem('data-format').value - const chartConfig = format === 'yaml' ? yaml.load(el.innerHTML) : JSON.parse(el.innerHTML) + const chartConfig = + format === 'yaml' + ? yaml.load(el.innerHTML) + : JSON.parse(el.innerHTML) el.innerHTML = '' const canvas = document.createElement('canvas') @@ -865,7 +917,12 @@ export default class MarkdownPreview extends React.Component { _.forEach( this.refs.root.contentWindow.document.querySelectorAll('.mermaid'), el => { - mermaidRender(el, htmlTextHelper.decodeEntities(el.innerHTML), theme, mermaidHTMLLabel) + mermaidRender( + el, + htmlTextHelper.decodeEntities(el.innerHTML), + theme, + mermaidHTMLLabel + ) } ) @@ -887,20 +944,14 @@ export default class MarkdownPreview extends React.Component { autoplay = 0 } - render( - , - el - ) + render(, el) } ) const markdownPreviewIframe = document.querySelector('.MarkdownPreview') const rect = markdownPreviewIframe.getBoundingClientRect() const config = { attributes: true, subtree: true } - const imgObserver = new MutationObserver((mutationList) => { + const imgObserver = new MutationObserver(mutationList => { for (const mu of mutationList) { if (mu.target.className === 'carouselContent-enter-done') { this.setImgOnClickEventHelper(mu.target, rect) @@ -909,26 +960,32 @@ export default class MarkdownPreview extends React.Component { } }) - const imgList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll('img') + const imgList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll( + 'img' + ) for (const img of imgList) { const parentEl = img.parentElement this.setImgOnClickEventHelper(img, rect) imgObserver.observe(parentEl, config) } - const aList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll('a') + const aList = markdownPreviewIframe.contentWindow.document.body.querySelectorAll( + 'a' + ) for (const a of aList) { a.removeEventListener('click', this.linkClickHandler) a.addEventListener('click', this.linkClickHandler) } } - setImgOnClickEventHelper (img, rect) { + setImgOnClickEventHelper(img, rect) { img.onclick = () => { const widthMagnification = document.body.clientWidth / img.width const heightMagnification = document.body.clientHeight / img.height const baseOnWidth = widthMagnification < heightMagnification - const magnification = baseOnWidth ? widthMagnification : heightMagnification + const magnification = baseOnWidth + ? widthMagnification + : heightMagnification const zoomImgWidth = img.width * magnification const zoomImgHeight = img.height * magnification @@ -959,10 +1016,7 @@ export default class MarkdownPreview extends React.Component { width: ${zoomImgWidth}; height: ${zoomImgHeight}px; ` - zoomImg.animate([ - originalImgRect, - zoomInImgRect - ], animationSpeed) + zoomImg.animate([originalImgRect, zoomInImgRect], animationSpeed) const overlay = document.createElement('div') overlay.style = ` @@ -983,10 +1037,10 @@ export default class MarkdownPreview extends React.Component { width: ${img.width}px; height: ${img.height}px; ` - const zoomOutImgAnimation = zoomImg.animate([ - zoomInImgRect, - originalImgRect - ], animationSpeed) + const zoomOutImgAnimation = zoomImg.animate( + [zoomInImgRect, originalImgRect], + animationSpeed + ) zoomOutImgAnimation.onfinish = () => overlay.remove() } @@ -995,11 +1049,20 @@ export default class MarkdownPreview extends React.Component { } } - focus () { + handleResize() { + _.forEach( + this.refs.root.contentWindow.document.querySelectorAll('svg[ratio]'), + el => { + el.setAttribute('height', el.clientWidth / el.getAttribute('ratio')) + } + ) + } + + focus() { this.refs.root.focus() } - getWindow () { + getWindow() { return this.refs.root.contentWindow } @@ -1007,7 +1070,7 @@ export default class MarkdownPreview extends React.Component { * @public * @param {Number} targetRow */ - scrollToRow (targetRow) { + scrollToRow(targetRow) { const blocks = this.getWindow().document.querySelectorAll( 'body>[data-line]' ) @@ -1028,16 +1091,16 @@ export default class MarkdownPreview extends React.Component { * @param {Number} x * @param {Number} y */ - scrollTo (x, y) { + scrollTo(x, y) { this.getWindow().document.body.scrollTo(x, y) } - preventImageDroppedHandler (e) { + preventImageDroppedHandler(e) { e.preventDefault() e.stopPropagation() } - notify (title, options) { + notify(title, options) { if (global.process.platform === 'win32') { options.icon = path.join( 'file://', @@ -1048,7 +1111,7 @@ export default class MarkdownPreview extends React.Component { return new window.Notification(title, options) } - handleLinkClick (e) { + handleLinkClick(e) { e.preventDefault() e.stopPropagation() @@ -1069,9 +1132,7 @@ export default class MarkdownPreview extends React.Component { if (posOfHash > -1) { const extractedId = linkHash.slice(posOfHash + 1) const targetId = mdurl.encode(extractedId) - const targetElement = this.getWindow().document.getElementById( - targetId - ) + const targetElement = this.getWindow().document.getElementById(targetId) if (targetElement != null) { this.scrollTo(0, targetElement.offsetTop) @@ -1109,10 +1170,21 @@ export default class MarkdownPreview extends React.Component { } // other case - shell.openExternal(href) + this.openExternal(href) } - render () { + openExternal(href) { + try { + const success = + shell.openExternal(href) || shell.openExternal(decodeURI(href)) + if (!success) console.error('failed to open url ' + href) + } catch (e) { + // URI Error threw from decodeURI + console.error(e) + } + } + + render() { const { className, style, tabIndex } = this.props return ( \n\n## Docs :memo:\n- [Boostnote | Boost your happiness, productivity and creativity.](https://hackernoon.com/boostnote-boost-your-happiness-productivity-and-creativity-315034efeebe)\n- [Cloud Syncing & Backups](https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup)\n- [How to sync your data across Desktop and Mobile apps](https://github.com/BoostIO/Boostnote/wiki/Sync-Data-Across-Desktop-and-Mobile-apps)\n- [Convert data from **Evernote** to Boostnote.](https://github.com/BoostIO/Boostnote/wiki/Evernote)\n- [Keyboard Shortcuts](https://github.com/BoostIO/Boostnote/wiki/Keyboard-Shortcuts)\n- [Keymaps in Editor mode](https://github.com/BoostIO/Boostnote/wiki/Keymaps-in-Editor-mode)\n- [How to set syntax highlight in Snippet note](https://github.com/BoostIO/Boostnote/wiki/Syntax-Highlighting)\n\n---\n\n## Article Archive :books:\n- [Reddit English](http://bit.ly/2mOJPu7)\n- [Reddit Spanish](https://www.reddit.com/r/boostnote_es/)\n- [Reddit Chinese](https://www.reddit.com/r/boostnote_cn/)\n- [Reddit Japanese](https://www.reddit.com/r/boostnote_jp/)\n\n---\n\n## Community :beers:\n- [GitHub](http://bit.ly/2AWWzkD)\n- [Twitter](http://bit.ly/2z8BUJZ)\n- [Facebook Group](http://bit.ly/2jcca8t)' + content: + '# Welcome to Boostnote!\n## Click here to edit markdown :wave:\n\n\n\n## Docs :memo:\n- [Boostnote | Boost your happiness, productivity and creativity.](https://hackernoon.com/boostnote-boost-your-happiness-productivity-and-creativity-315034efeebe)\n- [Cloud Syncing & Backups](https://github.com/BoostIO/Boostnote/wiki/Cloud-Syncing-and-Backup)\n- [How to sync your data across Desktop and Mobile apps](https://github.com/BoostIO/Boostnote/wiki/Sync-Data-Across-Desktop-and-Mobile-apps)\n- [Convert data from **Evernote** to Boostnote.](https://github.com/BoostIO/Boostnote/wiki/Evernote)\n- [Keyboard Shortcuts](https://github.com/BoostIO/Boostnote/wiki/Keyboard-Shortcuts)\n- [Keymaps in Editor mode](https://github.com/BoostIO/Boostnote/wiki/Keymaps-in-Editor-mode)\n- [How to set syntax highlight in Snippet note](https://github.com/BoostIO/Boostnote/wiki/Syntax-Highlighting)\n\n---\n\n## Article Archive :books:\n- [Reddit English](http://bit.ly/2mOJPu7)\n- [Reddit Spanish](https://www.reddit.com/r/boostnote_es/)\n- [Reddit Chinese](https://www.reddit.com/r/boostnote_cn/)\n- [Reddit Japanese](https://www.reddit.com/r/boostnote_jp/)\n\n---\n\n## Community :beers:\n- [GitHub](http://bit.ly/2AWWzkD)\n- [Twitter](http://bit.ly/2z8BUJZ)\n- [Facebook Group](http://bit.ly/2jcca8t)' }) .then(note => { store.dispatch({ @@ -140,7 +145,7 @@ class Main extends React.Component { }) } - componentDidMount () { + componentDidMount() { const { dispatch, config } = this.props this.refreshTheme = setInterval(() => { @@ -173,38 +178,44 @@ class Main extends React.Component { delete CodeMirror.keyMap.emacs['Ctrl-V'] eventEmitter.on('editor:fullscreen', this.toggleFullScreen) - eventEmitter.on('menubar:togglemenubar', this.toggleMenuBarVisible.bind(this)) + eventEmitter.on( + 'menubar:togglemenubar', + this.toggleMenuBarVisible.bind(this) + ) } - componentWillUnmount () { + componentWillUnmount() { eventEmitter.off('editor:fullscreen', this.toggleFullScreen) - eventEmitter.off('menubar:togglemenubar', this.toggleMenuBarVisible.bind(this)) + eventEmitter.off( + 'menubar:togglemenubar', + this.toggleMenuBarVisible.bind(this) + ) } - toggleMenuBarVisible () { + toggleMenuBarVisible() { const { config } = this.props const { ui } = config - const newUI = Object.assign(ui, {showMenuBar: !ui.showMenuBar}) + const newUI = Object.assign(ui, { showMenuBar: !ui.showMenuBar }) const newConfig = Object.assign(config, newUI) ConfigManager.set(newConfig) } - handleLeftSlideMouseDown (e) { + handleLeftSlideMouseDown(e) { e.preventDefault() this.setState({ isLeftSliderFocused: true }) } - handleRightSlideMouseDown (e) { + handleRightSlideMouseDown(e) { e.preventDefault() this.setState({ isRightSliderFocused: true }) } - handleMouseUp (e) { + handleMouseUp(e) { // Change width of NoteList component. if (this.state.isRightSliderFocused) { this.setState( @@ -244,7 +255,7 @@ class Main extends React.Component { } } - handleMouseMove (e) { + handleMouseMove(e) { if (this.state.isRightSliderFocused) { const offset = this.refs.body.getBoundingClientRect().left let newListWidth = e.pageX - offset @@ -270,7 +281,7 @@ class Main extends React.Component { } } - handleFullScreenButton (e) { + handleFullScreenButton(e) { this.setState({ fullScreen: !this.state.fullScreen }, () => { const noteDetail = document.querySelector('.NoteDetail') const noteList = document.querySelector('.NoteList') @@ -284,7 +295,7 @@ class Main extends React.Component { }) } - hideLeftLists (noteDetail, noteList, mainBody) { + hideLeftLists(noteDetail, noteList, mainBody) { this.setState({ noteDetailWidth: noteDetail.style.left }) this.setState({ mainBodyWidth: mainBody.style.left }) noteDetail.style.left = '0px' @@ -292,13 +303,13 @@ class Main extends React.Component { noteList.style.display = 'none' } - showLeftLists (noteDetail, noteList, mainBody) { + showLeftLists(noteDetail, noteList, mainBody) { noteDetail.style.left = this.state.noteDetailWidth mainBody.style.left = this.state.mainBodyWidth noteList.style.display = 'inline' } - render () { + render() { const { config } = this.props // the width of the navigation bar when it is folded/collapsed @@ -312,10 +323,16 @@ class Main extends React.Component { onMouseUp={e => this.handleMouseUp(e)} > - {!config.isSideNavFolded && + {!config.isSideNavFolded && (
-
} +
+ )}
- + + -
-
this.handleNoteListKeyDown(e)} + onKeyDown={e => this.handleNoteListKeyDown(e)} onKeyUp={this.handleNoteListKeyUp} onBlur={this.handleNoteListBlur} > diff --git a/browser/main/SideNav/ListButton.js b/browser/main/SideNav/ListButton.js index b5bc1488..dc9ba1cc 100644 --- a/browser/main/SideNav/ListButton.js +++ b/browser/main/SideNav/ListButton.js @@ -4,14 +4,17 @@ import CSSModules from 'browser/lib/CSSModules' import styles from './SwitchButton.styl' import i18n from 'browser/lib/i18n' -const ListButton = ({ - onClick, isTagActive -}) => ( - diff --git a/browser/main/SideNav/PreferenceButton.js b/browser/main/SideNav/PreferenceButton.js index 187bc41a..25499463 100644 --- a/browser/main/SideNav/PreferenceButton.js +++ b/browser/main/SideNav/PreferenceButton.js @@ -4,10 +4,8 @@ import CSSModules from 'browser/lib/CSSModules' import styles from './PreferenceButton.styl' import i18n from 'browser/lib/i18n' -const PreferenceButton = ({ - onClick -}) => ( - diff --git a/browser/main/SideNav/PreferenceButton.styl b/browser/main/SideNav/PreferenceButton.styl index 54513cb6..c404a604 100644 --- a/browser/main/SideNav/PreferenceButton.styl +++ b/browser/main/SideNav/PreferenceButton.styl @@ -1,52 +1,47 @@ -.top-menu-preference - navButtonColor() - position absolute - top 22px - right 10px - width 2em - background-color transparent - &:hover - color $ui-button-default--active-backgroundColor - background-color transparent - .tooltip - opacity 1 - &:active, &:active:hover - color $ui-button-default--active-backgroundColor - -body[data-theme="white"] - .top-menu-preference - navWhiteButtonColor() - background-color transparent - &:hover - color #0B99F1 - background-color transparent - &:active, &:active:hover - color #0B99F1 - background-color transparent - -body[data-theme="dark"] - .top-menu-preference - navDarkButtonColor() - background-color transparent - &:active - background-color alpha($ui-dark-button--active-backgroundColor, 20%) - background-color transparent - &:hover - background-color alpha($ui-dark-button--active-backgroundColor, 20%) - background-color transparent - - - -.tooltip - tooltip() - position absolute - pointer-events none - top 26px - left -20px - z-index 200 - padding 5px - line-height normal - border-radius 2px - opacity 0 - transition 0.1s - white-space nowrap +.top-menu-preference + navButtonColor() + width 2em + background-color transparent + &:hover + color $ui-button-default--active-backgroundColor + background-color transparent + .tooltip + opacity 1 + &:active, &:active:hover + color $ui-button-default--active-backgroundColor + +body[data-theme="white"] + .top-menu-preference + navWhiteButtonColor() + background-color transparent + &:hover + color #0B99F1 + background-color transparent + &:active, &:active:hover + color #0B99F1 + background-color transparent + +body[data-theme="dark"] + .top-menu-preference + navDarkButtonColor() + background-color transparent + &:active + background-color alpha($ui-dark-button--active-backgroundColor, 20%) + background-color transparent + &:hover + background-color alpha($ui-dark-button--active-backgroundColor, 20%) + background-color transparent + +.tooltip + tooltip() + position absolute + pointer-events none + top 26px + left -20px + z-index 200 + padding 5px + line-height normal + border-radius 2px + opacity 0 + transition 0.1s + white-space nowrap diff --git a/browser/main/SideNav/SearchButton.js b/browser/main/SideNav/SearchButton.js new file mode 100644 index 00000000..72d3cc78 --- /dev/null +++ b/browser/main/SideNav/SearchButton.js @@ -0,0 +1,26 @@ +import PropTypes from 'prop-types' +import React from 'react' +import CSSModules from 'browser/lib/CSSModules' +import styles from './SearchButton.styl' +import i18n from 'browser/lib/i18n' + +const SearchButton = ({ onClick, isActive }) => ( + +) + +SearchButton.propTypes = { + onClick: PropTypes.func.isRequired, + isActive: PropTypes.bool +} + +export default CSSModules(SearchButton, styles) diff --git a/browser/main/SideNav/SearchButton.styl b/browser/main/SideNav/SearchButton.styl new file mode 100644 index 00000000..76d4b806 --- /dev/null +++ b/browser/main/SideNav/SearchButton.styl @@ -0,0 +1,55 @@ +.top-menu-search + navButtonColor() + position relative + margin-right 6px + top 3px + width 2em + background-color transparent + &:hover + color $ui-button-default--active-backgroundColor + background-color transparent + .tooltip + opacity 1 + &:active, &:active:hover + color $ui-button-default--active-backgroundColor + +.icon-search + width 16px + +body[data-theme="white"] + .top-menu-search + navWhiteButtonColor() + background-color transparent + &:hover + color #0B99F1 + background-color transparent + &:active, &:active:hover + color #0B99F1 + background-color transparent + +body[data-theme="dark"] + .top-menu-search + navDarkButtonColor() + background-color transparent + &:active + background-color alpha($ui-dark-button--active-backgroundColor, 20%) + background-color transparent + &:hover + background-color alpha($ui-dark-button--active-backgroundColor, 20%) + background-color transparent + + + +.tooltip + tooltip() + position absolute + pointer-events none + top 26px + left -20px + z-index 200 + padding 5px + line-height normal + border-radius 2px + opacity 0 + transition 0.1s + white-space nowrap diff --git a/browser/main/SideNav/SideNav.styl b/browser/main/SideNav/SideNav.styl index 9fa6d4fa..b2a8a0d2 100644 --- a/browser/main/SideNav/SideNav.styl +++ b/browser/main/SideNav/SideNav.styl @@ -9,16 +9,47 @@ flex-direction column .top - padding-bottom 15px + display flex + align-items top + justify-content space-between + padding-bottom 10px + margin 14px 14px 4px .switch-buttons background-color transparent border 0 - margin 24px auto 4px 14px display flex + align-items center text-align center +.extra-buttons + position relative + display flex + align-items center +.search + position relative + flex 1 + display flex + max-height 0 + overflow hidden + transition max-height .4s + margin -5px 10px 0 + .search-input + flex 1 + height 2em + vertical-align middle + font-size 14px + border solid 1px $border-color + border-radius 2px + padding 2px 6px + outline none + .search-clear + width 10px + position absolute + right 8px + top 9px + cursor pointer .top-menu-label margin-left 5px @@ -68,8 +99,15 @@ background-color #2E3235 .switch-buttons display none + .extra-buttons > button:first-of-type // hide search icon + display none .top height 60px + align-items center + margin 0 + justify-content center + position relative + left -4px .top-menu position static width $sideNav--folded-width @@ -98,32 +136,52 @@ .top-menu-preference position absolute left 7px + .search + height 28px + .search-input + display none + .search-clear + display none + .search-folded + width 16px + padding-left 4px + margin-bottom 8px + cursor pointer body[data-theme="white"] .root, .root--folded background-color #f9f9f9 color $ui-text-color + .search .search-input + background-color #f9f9f9 + color $ui-text-color body[data-theme="dark"] .root, .root--folded border-right 1px solid $ui-dark-borderColor background-color $ui-dark-backgroundColor color $ui-dark-text-color + .search .search-input + background-color $ui-dark-backgroundColor + color $ui-dark-text-color + border-color $ui-dark-borderColor .top border-color $ui-dark-borderColor -body[data-theme="solarized-dark"] - .root, .root--folded - background-color $ui-solarized-dark-backgroundColor - border-right 1px solid $ui-solarized-dark-borderColor +apply-theme(theme) + body[data-theme={theme}] + .root, .root--folded + background-color get-theme-var(theme, 'backgroundColor') + border-right 1px solid get-theme-var(theme, 'borderColor') -body[data-theme="monokai"] - .root, .root--folded - background-color $ui-monokai-backgroundColor - border-right 1px solid $ui-monokai-borderColor + .search .search-input + background-color get-theme-var(theme, 'backgroundColor') + color get-theme-var(theme, 'text-color') + border-color get-theme-var(theme, 'borderColor') -body[data-theme="dracula"] - .root, .root--folded - background-color $ui-dracula-backgroundColor - border-right 1px solid $ui-dracula-borderColor \ No newline at end of file +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) + +for theme in $themes + apply-theme(theme) diff --git a/browser/main/SideNav/StorageItem.js b/browser/main/SideNav/StorageItem.js index 5cd4a491..a20b0df1 100644 --- a/browser/main/SideNav/StorageItem.js +++ b/browser/main/SideNav/StorageItem.js @@ -19,7 +19,7 @@ const escapeStringRegexp = require('escape-string-regexp') const path = require('path') class StorageItem extends React.Component { - constructor (props) { + constructor(props) { super(props) const { storage } = this.props @@ -30,11 +30,11 @@ class StorageItem extends React.Component { } } - handleHeaderContextMenu (e) { + handleHeaderContextMenu(e) { context.popup([ { label: i18n.__('Add Folder'), - click: (e) => this.handleAddFolderButtonClick(e) + click: e => this.handleAddFolderButtonClick(e) }, { type: 'separator' @@ -44,11 +44,11 @@ class StorageItem extends React.Component { submenu: [ { label: i18n.__('Export as txt'), - click: (e) => this.handleExportStorageClick(e, 'txt') + click: e => this.handleExportStorageClick(e, 'txt') }, { label: i18n.__('Export as md'), - click: (e) => this.handleExportStorageClick(e, 'md') + click: e => this.handleExportStorageClick(e, 'md') } ] }, @@ -57,75 +57,74 @@ class StorageItem extends React.Component { }, { label: i18n.__('Unlink Storage'), - click: (e) => this.handleUnlinkStorageClick(e) + click: e => this.handleUnlinkStorageClick(e) } ]) } - handleUnlinkStorageClick (e) { + handleUnlinkStorageClick(e) { const index = dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Unlink Storage'), - detail: i18n.__('This work will just detatches a storage from Boostnote. (Any data won\'t be deleted.)'), + detail: i18n.__( + "This work will just detatches a storage from Boostnote. (Any data won't be deleted.)" + ), buttons: [i18n.__('Confirm'), i18n.__('Cancel')] }) if (index === 0) { const { storage, dispatch } = this.props - dataApi.removeStorage(storage.key) + dataApi + .removeStorage(storage.key) .then(() => { dispatch({ type: 'REMOVE_STORAGE', storageKey: storage.key }) }) - .catch((err) => { + .catch(err => { throw err }) } } - handleExportStorageClick (e, fileType) { + handleExportStorageClick(e, fileType) { const options = { properties: ['openDirectory', 'createDirectory'], buttonLabel: i18n.__('Select directory'), title: i18n.__('Select a folder to export the files to'), multiSelections: false } - dialog.showOpenDialog(remote.getCurrentWindow(), options, - (paths) => { - if (paths && paths.length === 1) { - const { storage, dispatch } = this.props - dataApi - .exportStorage(storage.key, fileType, paths[0]) - .then(data => { - dispatch({ - type: 'EXPORT_STORAGE', - storage: data.storage, - fileType: data.fileType - }) - }) - } - }) + dialog.showOpenDialog(remote.getCurrentWindow(), options, paths => { + if (paths && paths.length === 1) { + const { storage, dispatch } = this.props + dataApi.exportStorage(storage.key, fileType, paths[0]).then(data => { + dispatch({ + type: 'EXPORT_STORAGE', + storage: data.storage, + fileType: data.fileType + }) + }) + } + }) } - handleToggleButtonClick (e) { + handleToggleButtonClick(e) { const { storage, dispatch } = this.props const isOpen = !this.state.isOpen - dataApi.toggleStorage(storage.key, isOpen) - .then((storage) => { - dispatch({ - type: 'EXPAND_STORAGE', - storage, - isOpen - }) + dataApi.toggleStorage(storage.key, isOpen).then(storage => { + dispatch({ + type: 'EXPAND_STORAGE', + storage, + isOpen }) + }) this.setState({ isOpen: isOpen }) } - handleAddFolderButtonClick (e) { + handleAddFolderButtonClick(e) { const { storage } = this.props modal.open(CreateFolderModal, { @@ -133,23 +132,23 @@ class StorageItem extends React.Component { }) } - handleHeaderInfoClick (e) { + handleHeaderInfoClick(e) { const { storage, dispatch } = this.props dispatch(push('/storages/' + storage.key)) } - handleFolderButtonClick (folderKey) { - return (e) => { + handleFolderButtonClick(folderKey) { + return e => { const { storage, dispatch } = this.props dispatch(push('/storages/' + storage.key + '/folders/' + folderKey)) } } - handleFolderButtonContextMenu (e, folder) { + handleFolderButtonContextMenu(e, folder) { context.popup([ { label: i18n.__('Rename Folder'), - click: (e) => this.handleRenameFolderClick(e, folder) + click: e => this.handleRenameFolderClick(e, folder) }, { type: 'separator' @@ -159,11 +158,11 @@ class StorageItem extends React.Component { submenu: [ { label: i18n.__('Export as txt'), - click: (e) => this.handleExportFolderClick(e, folder, 'txt') + click: e => this.handleExportFolderClick(e, folder, 'txt') }, { label: i18n.__('Export as md'), - click: (e) => this.handleExportFolderClick(e, folder, 'md') + click: e => this.handleExportFolderClick(e, folder, 'md') } ] }, @@ -172,12 +171,12 @@ class StorageItem extends React.Component { }, { label: i18n.__('Delete Folder'), - click: (e) => this.handleFolderDeleteClick(e, folder) + click: e => this.handleFolderDeleteClick(e, folder) } ]) } - handleRenameFolderClick (e, folder) { + handleRenameFolderClick(e, folder) { const { storage } = this.props modal.open(RenameFolderModal, { storage, @@ -185,20 +184,19 @@ class StorageItem extends React.Component { }) } - handleExportFolderClick (e, folder, fileType) { + handleExportFolderClick(e, folder, fileType) { const options = { properties: ['openDirectory', 'createDirectory'], buttonLabel: i18n.__('Select directory'), title: i18n.__('Select a folder to export the files to'), multiSelections: false } - dialog.showOpenDialog(remote.getCurrentWindow(), options, - (paths) => { + dialog.showOpenDialog(remote.getCurrentWindow(), options, paths => { if (paths && paths.length === 1) { const { storage, dispatch } = this.props dataApi .exportFolder(storage.key, folder.key, fileType, paths[0]) - .then((data) => { + .then(data => { dispatch({ type: 'EXPORT_FOLDER', storage: data.storage, @@ -224,66 +222,74 @@ class StorageItem extends React.Component { }) } - handleFolderDeleteClick (e, folder) { + handleFolderDeleteClick(e, folder) { const index = dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Delete Folder'), - detail: i18n.__('This will delete all notes in the folder and can not be undone.'), + detail: i18n.__( + 'This will delete all notes in the folder and can not be undone.' + ), buttons: [i18n.__('Confirm'), i18n.__('Cancel')] }) if (index === 0) { const { storage, dispatch } = this.props - dataApi - .deleteFolder(storage.key, folder.key) - .then((data) => { - dispatch({ - type: 'DELETE_FOLDER', - storage: data.storage, - folderKey: data.folderKey - }) + dataApi.deleteFolder(storage.key, folder.key).then(data => { + dispatch({ + type: 'DELETE_FOLDER', + storage: data.storage, + folderKey: data.folderKey }) + }) } } - handleDragEnter (e, key) { + handleDragEnter(e, key) { e.preventDefault() - if (this.state.draggedOver === key) { return } + if (this.state.draggedOver === key) { + return + } this.setState({ draggedOver: key }) } - handleDragLeave (e) { + handleDragLeave(e) { e.preventDefault() - if (this.state.draggedOver === null) { return } + if (this.state.draggedOver === null) { + return + } this.setState({ draggedOver: null }) } - dropNote (storage, folder, dispatch, location, noteData) { - noteData = noteData.filter((note) => folder.key !== note.folder) + dropNote(storage, folder, dispatch, location, noteData) { + noteData = noteData.filter(note => folder.key !== note.folder) if (noteData.length === 0) return Promise.all( - noteData.map((note) => dataApi.moveNote(note.storage, note.key, storage.key, folder.key)) + noteData.map(note => + dataApi.moveNote(note.storage, note.key, storage.key, folder.key) + ) ) - .then((createdNoteData) => { - createdNoteData.forEach((newNote) => { - dispatch({ - type: 'MOVE_NOTE', - originNote: noteData.find((note) => note.content === newNote.oldContent), - note: newNote + .then(createdNoteData => { + createdNoteData.forEach(newNote => { + dispatch({ + type: 'MOVE_NOTE', + originNote: noteData.find( + note => note.content === newNote.oldContent + ), + note: newNote + }) }) }) - }) - .catch((err) => { - console.error(`error on delete notes: ${err}`) - }) + .catch(err => { + console.error(`error on delete notes: ${err}`) + }) } - handleDrop (e, storage, folder, dispatch, location) { + handleDrop(e, storage, folder, dispatch, location) { e.preventDefault() if (this.state.draggedOver !== null) { this.setState({ @@ -294,21 +300,37 @@ class StorageItem extends React.Component { this.dropNote(storage, folder, dispatch, location, noteData) } - render () { + render() { const { storage, location, isFolded, data, dispatch } = this.props const { folderNoteMap, trashedSet } = data const SortableStorageItemChild = SortableElement(StorageItemChild) const folderList = storage.folders.map((folder, index) => { - const folderRegex = new RegExp(escapeStringRegexp(path.sep) + 'storages' + escapeStringRegexp(path.sep) + storage.key + escapeStringRegexp(path.sep) + 'folders' + escapeStringRegexp(path.sep) + folder.key) - const isActive = !!(location.pathname.match(folderRegex)) + const folderRegex = new RegExp( + escapeStringRegexp(path.sep) + + 'storages' + + escapeStringRegexp(path.sep) + + storage.key + + escapeStringRegexp(path.sep) + + 'folders' + + escapeStringRegexp(path.sep) + + folder.key + ) + const isActive = !!location.pathname.match(folderRegex) const noteSet = folderNoteMap.get(storage.key + '-' + folder.key) let noteCount = 0 if (noteSet) { let trashedNoteCount = 0 - const noteKeys = noteSet.map(noteKey => { return noteKey }) + const noteKeys = noteSet.map(noteKey => { + return noteKey + }) trashedSet.toJS().forEach(trashedKey => { - if (noteKeys.some(noteKey => { return noteKey === trashedKey })) trashedNoteCount++ + if ( + noteKeys.some(noteKey => { + return noteKey === trashedKey + }) + ) + trashedNoteCount++ }) noteCount = noteSet.size - trashedNoteCount } @@ -317,73 +339,80 @@ class StorageItem extends React.Component { key={folder.key} index={index} isActive={isActive || folder.key === this.state.draggedOver} - handleButtonClick={(e) => this.handleFolderButtonClick(folder.key)(e)} - handleContextMenu={(e) => this.handleFolderButtonContextMenu(e, folder)} + handleButtonClick={e => this.handleFolderButtonClick(folder.key)(e)} + handleContextMenu={e => this.handleFolderButtonContextMenu(e, folder)} folderName={folder.name} folderColor={folder.color} isFolded={isFolded} noteCount={noteCount} - handleDrop={(e) => { + handleDrop={e => { this.handleDrop(e, storage, folder, dispatch, location) }} - handleDragEnter={(e) => { + handleDragEnter={e => { this.handleDragEnter(e, folder.key) }} - handleDragLeave={(e) => { + handleDragLeave={e => { this.handleDragLeave(e, folder) }} /> ) }) - const isActive = location.pathname.match(new RegExp(escapeStringRegexp(path.sep) + 'storages' + escapeStringRegexp(path.sep) + storage.key + '$')) + const isActive = location.pathname.match( + new RegExp( + escapeStringRegexp(path.sep) + + 'storages' + + escapeStringRegexp(path.sep) + + storage.key + + '$' + ) + ) return ( -
-
this.handleHeaderContextMenu(e)} +
+
this.handleHeaderContextMenu(e)} > - - {!isFolded && - - } + )} -
- {this.state.isOpen && -
- {folderList} -
- } + {this.state.isOpen &&
{folderList}
}
) } diff --git a/browser/main/SideNav/StorageItem.styl b/browser/main/SideNav/StorageItem.styl index a06ecb11..375a989f 100644 --- a/browser/main/SideNav/StorageItem.styl +++ b/browser/main/SideNav/StorageItem.styl @@ -132,55 +132,57 @@ body[data-theme="white"] background-color alpha($ui-button--active-backgroundColor, 40%) color $ui-text-color -body[data-theme="dark"] - .header--active - background-color $ui-dark-button--active-backgroundColor - transition color background-color 0.15s +apply-theme(theme) + body[data-theme={theme}] + .header--active + background-color get-theme-var(theme, 'button--active-backgroundColor') + transition color background-color 0.15s + + .header--active + .header-toggleButton + color get-theme-var(theme, 'text-color') + + .header--active + .header-info + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button--active-backgroundColor') + &:active + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button--active-backgroundColor') + + .header--active + .header-addFolderButton + color get-theme-var(theme, 'text-color') - .header--active .header-toggleButton - color $ui-dark-text-color + &:hover + transition 0.2s + color get-theme-var(theme, 'text-color') + background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 60%) + &:active, &:active:hover + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button--active-backgroundColor') - .header--active .header-info - color $ui-dark-text-color - background-color $ui-dark-button--active-backgroundColor - &:active - color $ui-dark-text-color - background-color $ui-dark-button--active-backgroundColor + background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 20%) + &:hover + transition 0.2s + color get-theme-var(theme, 'text-color') + background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 20%) + &:active, &:active:hover + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button--active-backgroundColor') - .header--active .header-addFolderButton - color $ui-dark-text-color - - .header-toggleButton - &:hover - transition 0.2s - color $ui-dark-text-color - background-color alpha($ui-dark-button--active-backgroundColor, 60%) - &:active, &:active:hover - color $ui-dark-text-color - background-color $ui-dark-button--active-backgroundColor - - .header-info - background-color alpha($ui-dark-button--active-backgroundColor, 20%) - &:hover - transition 0.2s - color $ui-dark-text-color - background-color alpha($ui-dark-button--active-backgroundColor, 20%) - &:active, &:active:hover - color $ui-dark-text-color - background-color $ui-dark-button--active-backgroundColor - - .header-addFolderButton - &:hover - transition 0.2s - color $ui-dark-text-color - background-color alpha($ui-dark-button--active-backgroundColor, 60%) - &:active, &:active:hover - color $ui-dark-text-color - background-color $ui-dark-button--active-backgroundColor - - + &:hover + transition 0.2s + color get-theme-var(theme, 'text-color') + background-color alpha(get-theme-var(theme, 'button--active-backgroundColor'), 60%) + &:active, &:active:hover + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button--active-backgroundColor') +apply-theme('dark') +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/SideNav/SwitchButton.styl b/browser/main/SideNav/SwitchButton.styl index 36099140..2184bc69 100644 --- a/browser/main/SideNav/SwitchButton.styl +++ b/browser/main/SideNav/SwitchButton.styl @@ -1,60 +1,60 @@ -.non-active-button - color $ui-inactive-text-color - font-size 16px - border 0 - background-color transparent - transition 0.2s - display flex - text-align center - margin-right 4px - position relative - &:hover - color alpha(#239F86, 60%) - .tooltip - opacity 1 - -.active-button - @extend .non-active-button - color $ui-button-default--active-backgroundColor - -.tooltip - tooltip() - position absolute - pointer-events none - top 22px - left -2px - z-index 200 - padding 5px - line-height normal - border-radius 2px - opacity 0 - transition 0.1s - white-space nowrap - -body[data-theme="white"] - .non-active-button - color $ui-inactive-text-color - &:hover - color alpha(#0B99F1, 60%) - - .tag-title - p - color $ui-text-color - - .non-active-button - &:hover - color alpha(#0B99F1, 60%) - - .active-button - @extend .non-active-button - color #0B99F1 - -body[data-theme="dark"] - .non-active-button - color alpha($ui-dark-text-color, 60%) - &:hover - color alpha(#0B99F1, 60%) - - .tag-title - p +.non-active-button + color $ui-inactive-text-color + font-size 16px + border 0 + background-color transparent + transition 0.2s + display flex + text-align center + margin-right 4px + position relative + &:hover + color alpha(#239F86, 60%) + .tooltip + opacity 1 + +.active-button + @extend .non-active-button + color $ui-button-default--active-backgroundColor + +.tooltip + tooltip() + position absolute + pointer-events none + top 22px + left -2px + z-index 200 + padding 5px + line-height normal + border-radius 2px + opacity 0 + transition 0.1s + white-space nowrap + +body[data-theme="white"] + .non-active-button + color $ui-inactive-text-color + &:hover + color alpha(#0B99F1, 60%) + + .tag-title + p + color $ui-text-color + + .non-active-button + &:hover + color alpha(#0B99F1, 60%) + + .active-button + @extend .non-active-button + color #0B99F1 + +body[data-theme="dark"] + .non-active-button + color alpha($ui-dark-text-color, 60%) + &:hover + color alpha(#0B99F1, 60%) + + .tag-title + p color alpha($ui-dark-text-color, 60%) \ No newline at end of file diff --git a/browser/main/SideNav/TagButton.js b/browser/main/SideNav/TagButton.js index d91ae2c4..268e0d7e 100644 --- a/browser/main/SideNav/TagButton.js +++ b/browser/main/SideNav/TagButton.js @@ -4,14 +4,17 @@ import CSSModules from 'browser/lib/CSSModules' import styles from './SwitchButton.styl' import i18n from 'browser/lib/i18n' -const TagButton = ({ - onClick, isTagActive -}) => ( - diff --git a/browser/main/SideNav/index.js b/browser/main/SideNav/index.js index 241d4151..cd11e652 100644 --- a/browser/main/SideNav/index.js +++ b/browser/main/SideNav/index.js @@ -14,9 +14,10 @@ import StorageList from 'browser/components/StorageList' import NavToggleButton from 'browser/components/NavToggleButton' import EventEmitter from 'browser/main/lib/eventEmitter' import PreferenceButton from './PreferenceButton' +import SearchButton from './SearchButton' import ListButton from './ListButton' import TagButton from './TagButton' -import {SortableContainer} from 'react-sortable-hoc' +import { SortableContainer } from 'react-sortable-hoc' import i18n from 'browser/lib/i18n' import context from 'browser/lib/context' import { remote } from 'electron' @@ -24,13 +25,13 @@ import { confirmDeleteNote } from 'browser/lib/confirmDeleteNote' import ColorPicker from 'browser/components/ColorPicker' import { every, sortBy } from 'lodash' -function matchActiveTags (tags, activeTags) { +function matchActiveTags(tags, activeTags) { return every(activeTags, v => tags.indexOf(v) >= 0) } class SideNav extends React.Component { // TODO: should not use electron stuff v0.7 - constructor (props) { + constructor(props) { super(props) this.state = { @@ -38,33 +39,46 @@ class SideNav extends React.Component { show: false, color: null, tagName: null, - targetRect: null + targetRect: null, + showSearch: false, + searchText: '' } } this.dismissColorPicker = this.dismissColorPicker.bind(this) this.handleColorPickerConfirm = this.handleColorPickerConfirm.bind(this) this.handleColorPickerReset = this.handleColorPickerReset.bind(this) + this.handleSearchButtonClick = this.handleSearchButtonClick.bind(this) + this.handleSearchInputChange = this.handleSearchInputChange.bind(this) + this.handleSearchInputClear = this.handleSearchInputClear.bind(this) } - componentDidMount () { + componentDidMount() { EventEmitter.on('side:preferences', this.handleMenuButtonClick) } - componentWillUnmount () { + componentWillUnmount() { EventEmitter.off('side:preferences', this.handleMenuButtonClick) } - deleteTag (tag) { - const selectedButton = remote.dialog.showMessageBox(remote.getCurrentWindow(), { - type: 'warning', - message: i18n.__('Confirm tag deletion'), - detail: i18n.__('This will permanently remove this tag.'), - buttons: [i18n.__('Confirm'), i18n.__('Cancel')] - }) + deleteTag(tag) { + const selectedButton = remote.dialog.showMessageBox( + remote.getCurrentWindow(), + { + type: 'warning', + message: i18n.__('Confirm tag deletion'), + detail: i18n.__('This will permanently remove this tag.'), + buttons: [i18n.__('Confirm'), i18n.__('Cancel')] + } + ) if (selectedButton === 0) { - const { data, dispatch, location, match: { params } } = this.props + const { + data, + dispatch, + location, + match: { params } + } = this.props const notes = data.noteMap .map(note => note) @@ -78,44 +92,68 @@ class SideNav extends React.Component { return note }) - Promise - .all(notes.map(note => dataApi.updateNote(note.storage, note.key, note))) - .then(updatedNotes => { - updatedNotes.forEach(note => { - dispatch({ - type: 'UPDATE_NOTE', - note - }) + Promise.all( + notes.map(note => dataApi.updateNote(note.storage, note.key, note)) + ).then(updatedNotes => { + updatedNotes.forEach(note => { + dispatch({ + type: 'UPDATE_NOTE', + note }) - - if (location.pathname.match('/tags')) { - const tags = params.tagname.split(' ') - const index = tags.indexOf(tag) - if (index !== -1) { - tags.splice(index, 1) - - dispatch(push(`/tags/${tags.map(tag => encodeURIComponent(tag)).join(' ')}`)) - } - } }) + + if (location.pathname.match('/tags')) { + const tags = params.tagname.split(' ') + const index = tags.indexOf(tag) + if (index !== -1) { + tags.splice(index, 1) + + dispatch( + push( + `/tags/${tags.map(tag => encodeURIComponent(tag)).join(' ')}` + ) + ) + } + } + }) } } - handleMenuButtonClick (e) { + handleMenuButtonClick(e) { openModal(PreferencesModal) } - handleHomeButtonClick (e) { + handleSearchButtonClick(e) { + const { showSearch } = this.state + this.setState({ + showSearch: !showSearch, + searchText: '' + }) + } + + handleSearchInputClear(e) { + this.setState({ + searchText: '' + }) + } + + handleSearchInputChange(e) { + this.setState({ + searchText: e.target.value + }) + } + + handleHomeButtonClick(e) { const { dispatch } = this.props dispatch(push('/home')) } - handleStarredButtonClick (e) { + handleStarredButtonClick(e) { const { dispatch } = this.props dispatch(push('/starred')) } - handleTagContextMenu (e, tag) { + handleTagContextMenu(e, tag) { const menu = [] menu.push({ @@ -125,13 +163,17 @@ class SideNav extends React.Component { menu.push({ label: i18n.__('Customize Color'), - click: this.displayColorPicker.bind(this, tag, e.target.getBoundingClientRect()) + click: this.displayColorPicker.bind( + this, + tag, + e.target.getBoundingClientRect() + ) }) context.popup(menu) } - dismissColorPicker () { + dismissColorPicker() { this.setState({ colorPicker: { show: false @@ -139,7 +181,7 @@ class SideNav extends React.Component { }) } - displayColorPicker (tagName, rect) { + displayColorPicker(tagName, rect) { const { config } = this.props this.setState({ colorPicker: { @@ -151,10 +193,17 @@ class SideNav extends React.Component { }) } - handleColorPickerConfirm (color) { - const { dispatch, config: {coloredTags} } = this.props - const { colorPicker: { tagName } } = this.state - const newColoredTags = Object.assign({}, coloredTags, {[tagName]: color.hex}) + handleColorPickerConfirm(color) { + const { + dispatch, + config: { coloredTags } + } = this.props + const { + colorPicker: { tagName } + } = this.state + const newColoredTags = Object.assign({}, coloredTags, { + [tagName]: color.hex + }) const config = { coloredTags: newColoredTags } ConfigManager.set(config) @@ -165,9 +214,14 @@ class SideNav extends React.Component { this.dismissColorPicker() } - handleColorPickerReset () { - const { dispatch, config: {coloredTags} } = this.props - const { colorPicker: { tagName } } = this.state + handleColorPickerReset() { + const { + dispatch, + config: { coloredTags } + } = this.props + const { + colorPicker: { tagName } + } = this.state const newColoredTags = Object.assign({}, coloredTags) delete newColoredTags[tagName] @@ -181,44 +235,50 @@ class SideNav extends React.Component { this.dismissColorPicker() } - handleToggleButtonClick (e) { + handleToggleButtonClick(e) { const { dispatch, config } = this.props + const { showSearch, searchText } = this.state - ConfigManager.set({isSideNavFolded: !config.isSideNavFolded}) + ConfigManager.set({ isSideNavFolded: !config.isSideNavFolded }) dispatch({ type: 'SET_IS_SIDENAV_FOLDED', isFolded: !config.isSideNavFolded }) + + if (showSearch && searchText.length === 0) { + this.setState({ + showSearch: false + }) + } } - handleTrashedButtonClick (e) { + handleTrashedButtonClick(e) { const { dispatch } = this.props dispatch(push('/trashed')) } - handleSwitchFoldersButtonClick () { + handleSwitchFoldersButtonClick() { const { dispatch } = this.props dispatch(push('/home')) } - handleSwitchTagsButtonClick () { + handleSwitchTagsButtonClick() { const { dispatch } = this.props dispatch(push('/alltags')) } - onSortEnd (storage) { - return ({oldIndex, newIndex}) => { + onSortEnd(storage) { + return ({ oldIndex, newIndex }) => { const { dispatch } = this.props - dataApi - .reorderFolder(storage.key, oldIndex, newIndex) - .then((data) => { - dispatch({ type: 'REORDER_FOLDER', storage: data.storage }) - }) + dataApi.reorderFolder(storage.key, oldIndex, newIndex).then(data => { + dispatch({ type: 'REORDER_FOLDER', storage: data.storage }) + }) } } - SideNavComponent (isFolded, storageList) { - const { location, data, config } = this.props + SideNavComponent(isFolded) { + const { location, data, config, dispatch } = this.props + const { showSearch, searchText } = this.state const isHomeActive = !!location.pathname.match(/^\/home$/) const isStarredActive = !!location.pathname.match(/^\/starred$/) @@ -227,25 +287,62 @@ class SideNav extends React.Component { let component // TagsMode is not selected - if (!location.pathname.match('/tags') && !location.pathname.match('/alltags')) { + if ( + !location.pathname.match('/tags') && + !location.pathname.match('/alltags') + ) { + let storageMap = data.storageMap + if (showSearch && searchText.length > 0) { + storageMap = storageMap.map(storage => { + const folders = storage.folders.filter( + folder => + folder.name.toLowerCase().indexOf(searchText.toLowerCase()) !== -1 + ) + return Object.assign({}, storage, { folders }) + }) + } + + const storageList = storageMap.map((storage, key) => { + const SortableStorageItem = SortableContainer(StorageItem) + return ( + + ) + }) + component = (
this.handleHomeButtonClick(e)} + handleAllNotesButtonClick={e => this.handleHomeButtonClick(e)} isStarredActive={isStarredActive} isTrashedActive={isTrashedActive} - handleStarredButtonClick={(e) => this.handleStarredButtonClick(e)} - handleTrashedButtonClick={(e) => this.handleTrashedButtonClick(e)} - counterTotalNote={data.noteMap._map.size - data.trashedSet._set.size} + handleStarredButtonClick={e => this.handleStarredButtonClick(e)} + handleTrashedButtonClick={e => this.handleTrashedButtonClick(e)} + counterTotalNote={ + data.noteMap._map.size - data.trashedSet._set.size + } counterStarredNote={data.starredSet._set.size} counterDelNote={data.trashedSet._set.size} - handleFilterButtonContextMenu={this.handleFilterButtonContextMenu.bind(this)} + handleFilterButtonContextMenu={this.handleFilterButtonContextMenu.bind( + this + )} /> - +
) } else { @@ -257,22 +354,26 @@ class SideNav extends React.Component {
-
-
- {this.tagListComponent(data)} -
- +
{this.tagListComponent(data)}
+
) } @@ -280,82 +381,89 @@ class SideNav extends React.Component { return component } - tagListComponent () { + tagListComponent() { const { data, location, config } = this.props - const { colorPicker } = this.state + const { colorPicker, showSearch, searchText } = this.state const activeTags = this.getActiveTags(location.pathname) const relatedTags = this.getRelatedTags(activeTags, data.noteMap) - let tagList = sortBy(data.tagNoteMap.map( - (tag, name) => ({ name, size: tag.size, related: relatedTags.has(name) }) - ).filter( - tag => tag.size > 0 - ), ['name']) + let tagList = sortBy( + data.tagNoteMap + .map((tag, name) => ({ + name, + size: tag.size, + related: relatedTags.has(name) + })) + .filter(tag => tag.size > 0), + ['name'] + ) + if (showSearch && searchText.length > 0) { + tagList = tagList.filter( + tag => tag.name.toLowerCase().indexOf(searchText.toLowerCase()) !== -1 + ) + } if (config.ui.enableLiveNoteCounts && activeTags.length !== 0) { const notesTags = data.noteMap.map(note => note.tags) tagList = tagList.map(tag => { - tag.size = notesTags.filter(tags => tags.includes(tag.name) && matchActiveTags(tags, activeTags)).length + tag.size = notesTags.filter( + tags => tags.includes(tag.name) && matchActiveTags(tags, activeTags) + ).length return tag }) } if (config.sortTagsBy === 'COUNTER') { - tagList = sortBy(tagList, item => (0 - item.size)) + tagList = sortBy(tagList, item => 0 - item.size) } - if (config.ui.showOnlyRelatedTags && (relatedTags.size > 0)) { - tagList = tagList.filter( - tag => tag.related + if (config.ui.showOnlyRelatedTags && relatedTags.size > 0) { + tagList = tagList.filter(tag => tag.related) + } + return tagList.map(tag => { + return ( + ) - } - return ( - tagList.map(tag => { - return ( - - ) - }) - ) + }) } - getRelatedTags (activeTags, noteMap) { + getRelatedTags(activeTags, noteMap) { if (activeTags.length === 0) { return new Set() } - const relatedNotes = noteMap.map( - note => ({key: note.key, tags: note.tags}) - ).filter( - note => activeTags.every(tag => note.tags.includes(tag)) - ) + const relatedNotes = noteMap + .map(note => ({ key: note.key, tags: note.tags })) + .filter(note => activeTags.every(tag => note.tags.includes(tag))) const relatedTags = new Set() relatedNotes.forEach(note => note.tags.map(tag => relatedTags.add(tag))) return relatedTags } - getTagActive (path, tag) { + getTagActive(path, tag) { return this.getActiveTags(path).includes(tag) } - getActiveTags (path) { + getActiveTags(path) { const pathSegments = path.split('/') const tags = pathSegments[pathSegments.length - 1] - return (tags === 'alltags') - ? [] - : decodeURIComponent(tags).split(' ') + return tags === 'alltags' ? [] : decodeURIComponent(tags).split(' ') } - handleClickTagListItem (name) { + handleClickTagListItem(name) { const { dispatch } = this.props dispatch(push(`/tags/${encodeURIComponent(name)}`)) } - handleSortTagsByChange (e) { + handleSortTagsByChange(e) { const { dispatch } = this.props const config = { @@ -369,7 +477,7 @@ class SideNav extends React.Component { }) } - handleClickNarrowToTag (tag) { + handleClickNarrowToTag(tag) { const { dispatch, location } = this.props const listOfTags = this.getActiveTags(location.pathname) const indexOfTag = listOfTags.indexOf(tag) @@ -381,51 +489,40 @@ class SideNav extends React.Component { dispatch(push(`/tags/${encodeURIComponent(listOfTags.join(' '))}`)) } - emptyTrash (entries) { + emptyTrash(entries) { const { dispatch } = this.props - const deletionPromises = entries.map((note) => { + const deletionPromises = entries.map(note => { return dataApi.deleteNote(note.storage, note.key) }) const { confirmDeletion } = this.props.config.ui if (!confirmDeleteNote(confirmDeletion, true)) return Promise.all(deletionPromises) - .then((arrayOfStorageAndNoteKeys) => { - arrayOfStorageAndNoteKeys.forEach(({ storageKey, noteKey }) => { - dispatch({ type: 'DELETE_NOTE', storageKey, noteKey }) + .then(arrayOfStorageAndNoteKeys => { + arrayOfStorageAndNoteKeys.forEach(({ storageKey, noteKey }) => { + dispatch({ type: 'DELETE_NOTE', storageKey, noteKey }) + }) + }) + .catch(err => { + console.error('Cannot Delete note: ' + err) }) - }) - .catch((err) => { - console.error('Cannot Delete note: ' + err) - }) } - handleFilterButtonContextMenu (event) { + handleFilterButtonContextMenu(event) { const { data } = this.props - const trashedNotes = data.trashedSet.toJS().map((uniqueKey) => data.noteMap.get(uniqueKey)) + const trashedNotes = data.trashedSet + .toJS() + .map(uniqueKey => data.noteMap.get(uniqueKey)) context.popup([ - { label: i18n.__('Empty Trash'), click: () => this.emptyTrash(trashedNotes) } + { + label: i18n.__('Empty Trash'), + click: () => this.emptyTrash(trashedNotes) + } ]) } - render () { - const { data, location, config, dispatch } = this.props - const { colorPicker: colorPickerState } = this.state - - const isFolded = config.isSideNavFolded - - const storageList = data.storageMap.map((storage, key) => { - const SortableStorageItem = SortableContainer(StorageItem) - return - }) + render() { + const { location, config } = this.props + const { showSearch, searchText, colorPicker: colorPickerState } = this.state let colorPicker if (colorPickerState.show) { @@ -440,25 +537,63 @@ class SideNav extends React.Component { ) } + const isFolded = config.isSideNavFolded const style = {} if (!isFolded) style.width = this.props.width const isTagActive = /tag/.test(location.pathname) + + const navSearch = ( +
+ + + {isFolded && ( + + )} +
+ ) + return ( -
- - + +
-
+
+
- {this.SideNavComponent(isFolded, storageList)} + {navSearch} + {this.SideNavComponent(isFolded)} {colorPicker}
) diff --git a/browser/main/StatusBar/StatusBar.styl b/browser/main/StatusBar/StatusBar.styl index 23dec208..0ff3e7e5 100644 --- a/browser/main/StatusBar/StatusBar.styl +++ b/browser/main/StatusBar/StatusBar.styl @@ -78,24 +78,19 @@ body[data-theme="dark"] border-color $ui-dark-borderColor border-left 1px solid $ui-dark-borderColor -body[data-theme="monokai"] - navButtonColor() - .zoom - border-color $ui-dark-borderColor - color $ui-monokai-text-color - &:hover - transition 0.15s - color $ui-monokai-active-color - &:active - color $ui-monokai-active-color +apply-theme(theme) + body[data-theme={theme}] + .zoom + border-color $ui-dark-borderColor + color get-theme-var(theme, 'text-color') + &:hover + transition 0.15s + color get-theme-var(theme, 'active-color') + &:active + color get-theme-var(theme, 'active-color') -body[data-theme="dracula"] - navButtonColor() - .zoom - border-color $ui-dark-borderColor - color $ui-dracula-text-color - &:hover - transition 0.15s - color $ui-dracula-active-color - &:active - color $ui-dracula-active-color \ No newline at end of file +for theme in 'dracula' 'solarized-dark' + apply-theme(theme) + +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/StatusBar/index.js b/browser/main/StatusBar/index.js index c99bf036..6b53f2d2 100644 --- a/browser/main/StatusBar/index.js +++ b/browser/main/StatusBar/index.js @@ -11,30 +11,43 @@ const electron = require('electron') const { remote, ipcRenderer } = electron const { dialog } = remote -const zoomOptions = [0.8, 0.9, 1, 1.1, 1.2, 1.3, 1.4, 1.5, 1.6, 1.7, 1.8, 1.9, 2.0] +const zoomOptions = [ + 0.8, + 0.9, + 1, + 1.1, + 1.2, + 1.3, + 1.4, + 1.5, + 1.6, + 1.7, + 1.8, + 1.9, + 2.0 +] class StatusBar extends React.Component { - - constructor (props) { + constructor(props) { super(props) this.handleZoomInMenuItem = this.handleZoomInMenuItem.bind(this) this.handleZoomOutMenuItem = this.handleZoomOutMenuItem.bind(this) this.handleZoomResetMenuItem = this.handleZoomResetMenuItem.bind(this) } - componentDidMount () { + componentDidMount() { EventEmitter.on('status:zoomin', this.handleZoomInMenuItem) EventEmitter.on('status:zoomout', this.handleZoomOutMenuItem) EventEmitter.on('status:zoomreset', this.handleZoomResetMenuItem) } - componentWillUnmount () { + componentWillUnmount() { EventEmitter.off('status:zoomin', this.handleZoomInMenuItem) EventEmitter.off('status:zoomout', this.handleZoomOutMenuItem) EventEmitter.off('status:zoomreset', this.handleZoomResetMenuItem) } - updateApp () { + updateApp() { const index = dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Update Boostnote'), @@ -47,10 +60,10 @@ class StatusBar extends React.Component { } } - handleZoomButtonClick (e) { + handleZoomButtonClick(e) { const templates = [] - zoomOptions.forEach((zoom) => { + zoomOptions.forEach(zoom => { templates.push({ label: Math.floor(zoom * 100) + '%', click: () => this.handleZoomMenuItemClick(zoom) @@ -60,7 +73,7 @@ class StatusBar extends React.Component { context.popup(templates) } - handleZoomMenuItemClick (zoomFactor) { + handleZoomMenuItemClick(zoomFactor) { const { dispatch } = this.props ZoomManager.setZoom(zoomFactor) dispatch({ @@ -69,40 +82,36 @@ class StatusBar extends React.Component { }) } - handleZoomInMenuItem () { + handleZoomInMenuItem() { const zoomFactor = ZoomManager.getZoom() + 0.1 this.handleZoomMenuItemClick(zoomFactor) } - handleZoomOutMenuItem () { + handleZoomOutMenuItem() { const zoomFactor = ZoomManager.getZoom() - 0.1 this.handleZoomMenuItemClick(zoomFactor) } - handleZoomResetMenuItem () { + handleZoomResetMenuItem() { this.handleZoomMenuItemClick(1.0) } - render () { + render() { const { config, status } = this.context return ( -
- - {status.updateReady - ? - : null - } + ) : null}
) } diff --git a/browser/main/TopBar/TopBar.styl b/browser/main/TopBar/TopBar.styl index 61b21fc5..a0eeadf6 100644 --- a/browser/main/TopBar/TopBar.styl +++ b/browser/main/TopBar/TopBar.styl @@ -212,69 +212,31 @@ body[data-theme="dark"] .control-newPostButton-tooltip darkTooltip() +apply-theme(theme) + body[data-theme={theme}] + .root, .root--expanded + background-color get-theme-var(theme, 'noteList-backgroundColor') -body[data-theme="solarized-dark"] - .root, .root--expanded - background-color $ui-solarized-dark-noteList-backgroundColor + .control + border-color get-theme-var(theme, 'borderColor') + .control-search + background-color get-theme-var(theme, 'noteList-backgroundColor') - .control - border-color $ui-solarized-dark-borderColor - .control-search - background-color $ui-solarized-dark-noteList-backgroundColor + .control-search-icon + absolute top bottom left + line-height 32px + width 35px + color get-theme-var(theme, 'inactive-text-color') + background-color get-theme-var(theme, 'noteList-backgroundColor') - .control-search-icon - absolute top bottom left - line-height 32px - width 35px - color $ui-solarized-dark-inactive-text-color - background-color $ui-solarized-dark-noteList-backgroundColor + .control-search-input + background-color get-theme-var(theme, 'noteList-backgroundColor') + input + background-color get-theme-var(theme, 'noteList-backgroundColor') + color get-theme-var(theme, 'text-color') - .control-search-input - background-color $ui-solarized-dark-noteList-backgroundColor - input - background-color $ui-solarized-dark-noteList-backgroundColor - color $ui-solarized-dark-text-color +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="monokai"] - .root, .root--expanded - background-color $ui-monokai-noteList-backgroundColor - - .control - border-color $ui-monokai-borderColor - .control-search - background-color $ui-monokai-noteList-backgroundColor - - .control-search-icon - absolute top bottom left - line-height 32px - width 35px - color $ui-monokai-inactive-text-color - background-color $ui-monokai-noteList-backgroundColor - - .control-search-input - background-color $ui-monokai-noteList-backgroundColor - input - background-color $ui-monokai-noteList-backgroundColor - color $ui-monokai-text-color - -body[data-theme="dracula"] - .root, .root--expanded - background-color $ui-dracula-noteList-backgroundColor - - .control - border-color $ui-dracula-borderColor - .control-search - background-color $ui-dracula-noteList-backgroundColor - - .control-search-icon - absolute top bottom left - line-height 32px - width 35px - color $ui-dracula-inactive-text-color - background-color $ui-dracula-noteList-backgroundColor - - .control-search-input - background-color $ui-dracula-noteList-backgroundColor - input - background-color $ui-dracula-noteList-backgroundColor - color $ui-dracula-text-color \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/TopBar/index.js b/browser/main/TopBar/index.js index 09fd56b2..e9554a67 100644 --- a/browser/main/TopBar/index.js +++ b/browser/main/TopBar/index.js @@ -11,7 +11,7 @@ import CInput from 'react-composition-input' import { push } from 'connected-react-router' class TopBar extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -33,19 +33,25 @@ class TopBar extends React.Component { this.handleSearchChange = this.handleSearchChange.bind(this) this.handleSearchClearButton = this.handleSearchClearButton.bind(this) - this.debouncedUpdateKeyword = debounce((keyword) => { - dispatch(push(`/searched/${encodeURIComponent(keyword)}`)) - this.setState({ - search: keyword - }) - ee.emit('top:search', keyword) - }, 1000 / 60, { - maxWait: 1000 / 8 - }) + this.debouncedUpdateKeyword = debounce( + keyword => { + dispatch(push(`/searched/${encodeURIComponent(keyword)}`)) + this.setState({ + search: keyword + }) + ee.emit('top:search', keyword) + }, + 1000 / 60, + { + maxWait: 1000 / 8 + } + ) } - componentDidMount () { - const { match: { params } } = this.props + componentDidMount() { + const { + match: { params } + } = this.props const searchWord = params && params.searchword if (searchWord !== undefined) { this.setState({ @@ -57,12 +63,12 @@ class TopBar extends React.Component { ee.on('code:init', this.codeInitHandler) } - componentWillUnmount () { + componentWillUnmount() { ee.off('top:focus-search', this.focusSearchHandler) ee.off('code:init', this.codeInitHandler) } - handleSearchClearButton (e) { + handleSearchClearButton(e) { const { dispatch } = this.props this.setState({ search: '', @@ -74,7 +80,7 @@ class TopBar extends React.Component { this.debouncedUpdateKeyword('') } - handleKeyDown (e) { + handleKeyDown(e) { // Re-apply search field on ENTER key if (e.keyCode === 13) { this.debouncedUpdateKeyword(e.target.value) @@ -98,18 +104,18 @@ class TopBar extends React.Component { } } - handleSearchChange (e) { + handleSearchChange(e) { const keyword = e.target.value this.debouncedUpdateKeyword(keyword) } - handleSearchFocus (e) { + handleSearchFocus(e) { this.setState({ isSearching: true }) } - handleSearchBlur (e) { + handleSearchBlur(e) { e.stopPropagation() let el = e.relatedTarget @@ -128,7 +134,7 @@ class TopBar extends React.Component { } } - handleOnSearchFocus () { + handleOnSearchFocus() { const el = this.refs.search.childNodes[0] if (this.state.isSearching) { el.blur() @@ -137,20 +143,22 @@ class TopBar extends React.Component { } } - handleCodeInit () { + handleCodeInit() { ee.emit('top:search', this.refs.searchInput.value || '') } - render () { + render() { const { config, style, location } = this.props return ( -
-
- {this.state.search !== '' && - - } + )}
- {location.pathname === '/trashed' ? '' - : } + {location.pathname === '/trashed' ? ( + '' + ) : ( + + )}
) } diff --git a/browser/main/global.styl b/browser/main/global.styl index d864993d..3f513c78 100644 --- a/browser/main/global.styl +++ b/browser/main/global.styl @@ -96,16 +96,6 @@ modalBackColor = white z-index modalZIndex + 1 -body[data-theme="dark"] - background-color $ui-dark-backgroundColor - ::-webkit-scrollbar-thumb - background-color rgba(0, 0, 0, 0.3) - .ModalBase - .modalBack - background-color $ui-dark-backgroundColor - .sortableItemHelper - color: $ui-dark-text-color - .CodeMirror font-family inherit !important line-height 1.4em @@ -148,38 +138,25 @@ body[data-theme="dark"] .sortableItemHelper z-index modalZIndex + 5 -body[data-theme="solarized-dark"] - background-color $ui-solarized-dark-backgroundColor - ::-webkit-scrollbar-thumb - background-color rgba(0, 0, 0, 0.3) - .ModalBase - .modalBack - background-color $ui-solarized-dark-backgroundColor - .sortableItemHelper - color: $ui-solarized-dark-text-color +apply-theme(theme) + body[data-theme={theme}] + background-color get-theme-var(theme, 'backgroundColor') + ::-webkit-scrollbar-thumb + background-color rgba(0, 0, 0, 0.3) + .ModalBase + .modalBack + background-color get-theme-var(theme, 'backgroundColor') + .sortableItemHelper + color get-theme-var(theme, 'text-color') -body[data-theme="monokai"] - background-color $ui-monokai-backgroundColor - ::-webkit-scrollbar-thumb - background-color rgba(0, 0, 0, 0.3) - .ModalBase - .modalBack - background-color $ui-monokai-backgroundColor - .sortableItemHelper - color: $ui-monokai-text-color +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="dracula"] - background-color $ui-dracula-backgroundColor - ::-webkit-scrollbar-thumb - background-color rgba(0, 0, 0, 0.3) - .ModalBase - .modalBack - background-color $ui-dracula-backgroundColor - .sortableItemHelper - color: $ui-dracula-text-color +for theme in $themes + apply-theme(theme) body[data-theme="default"] .SideNav ::-webkit-scrollbar-thumb background-color rgba(255, 255, 255, 0.3) -@import '../styles/Detail/TagSelect.styl' \ No newline at end of file +@import '../styles/Detail/TagSelect.styl' diff --git a/browser/main/index.js b/browser/main/index.js index b3a909e5..e77c62dd 100644 --- a/browser/main/index.js +++ b/browser/main/index.js @@ -4,6 +4,7 @@ import { store, history } from './store' import React, { Fragment } from 'react' import ReactDOM from 'react-dom' require('!!style!css!stylus?sourceMap!./global.styl') +import config from 'browser/main/lib/ConfigManager' import { Route, Switch, Redirect } from 'react-router-dom' import { ConnectedRouter } from 'connected-react-router' import DevTools from './DevTools' @@ -17,11 +18,11 @@ const electron = require('electron') const { remote, ipcRenderer } = electron const { dialog } = remote -document.addEventListener('drop', function (e) { +document.addEventListener('drop', function(e) { e.preventDefault() e.stopPropagation() }) -document.addEventListener('dragover', function (e) { +document.addEventListener('dragover', function(e) { e.preventDefault() e.stopPropagation() }) @@ -33,7 +34,7 @@ let isAltWithMouse = false let isAltWithOtherKey = false let isOtherKey = false -document.addEventListener('keydown', function (e) { +document.addEventListener('keydown', function(e) { if (e.key === 'Alt') { isAltPressing = true if (isOtherKey) { @@ -47,13 +48,13 @@ document.addEventListener('keydown', function (e) { } }) -document.addEventListener('mousedown', function (e) { +document.addEventListener('mousedown', function(e) { if (isAltPressing) { isAltWithMouse = true } }) -document.addEventListener('keyup', function (e) { +document.addEventListener('keyup', function(e) { if (e.key === 'Alt') { if (isAltWithMouse || isAltWithOtherKey) { e.preventDefault() @@ -65,26 +66,35 @@ document.addEventListener('keyup', function (e) { } }) -document.addEventListener('click', function (e) { +document.addEventListener('click', function(e) { const className = e.target.className - if (!className && typeof (className) !== 'string') return + if (!className && typeof className !== 'string') return const isInfoButton = className.includes('infoButton') const offsetParent = e.target.offsetParent - const isInfoPanel = offsetParent !== null - ? offsetParent.className.includes('infoPanel') - : false + const isInfoPanel = + offsetParent !== null ? offsetParent.className.includes('infoPanel') : false if (isInfoButton || isInfoPanel) return const infoPanel = document.querySelector('.infoPanel') if (infoPanel) infoPanel.style.display = 'none' }) +if (!config.get().ui.showScrollBar) { + document.styleSheets[54].insertRule('::-webkit-scrollbar {display: none}') + document.styleSheets[54].insertRule( + '::-webkit-scrollbar-corner {display: none}' + ) + document.styleSheets[54].insertRule( + '::-webkit-scrollbar-thumb {display: none}' + ) +} + const el = document.getElementById('content') -function notify (...args) { +function notify(...args) { return new window.Notification(...args) } -function updateApp () { +function updateApp() { const index = dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Update Boostnote'), @@ -97,7 +107,7 @@ function updateApp () { } } -ReactDOM.render(( +ReactDOM.render( @@ -112,36 +122,41 @@ ReactDOM.render(( {/* storages */} - + - -), el, function () { - const loadingCover = document.getElementById('loadingCover') - loadingCover.parentNode.removeChild(loadingCover) + , + el, + function() { + const loadingCover = document.getElementById('loadingCover') + loadingCover.parentNode.removeChild(loadingCover) - ipcRenderer.on('update-ready', function () { - store.dispatch({ - type: 'UPDATE_AVAILABLE' + ipcRenderer.on('update-ready', function() { + store.dispatch({ + type: 'UPDATE_AVAILABLE' + }) + notify('Update ready!', { + body: 'New Boostnote is ready to be installed.' + }) + updateApp() }) - notify('Update ready!', { - body: 'New Boostnote is ready to be installed.' - }) - updateApp() - }) - ipcRenderer.on('update-found', function () { - notify('Update found!', { - body: 'Preparing to update...' + ipcRenderer.on('update-found', function() { + notify('Update found!', { + body: 'Preparing to update...' + }) }) - }) - ipcRenderer.send('update-check', 'check-update') - window.addEventListener('online', function () { - if (!store.getState().status.updateReady) { - ipcRenderer.send('update-check', 'check-update') - } - }) -}) + ipcRenderer.send('update-check', 'check-update') + window.addEventListener('online', function() { + if (!store.getState().status.updateReady) { + ipcRenderer.send('update-check', 'check-update') + } + }) + } +) diff --git a/browser/main/lib/AwsMobileAnalyticsConfig.js b/browser/main/lib/AwsMobileAnalyticsConfig.js index e4a21a92..ce7b03ef 100644 --- a/browser/main/lib/AwsMobileAnalyticsConfig.js +++ b/browser/main/lib/AwsMobileAnalyticsConfig.js @@ -22,7 +22,7 @@ if (!getSendEventCond()) { }) } -function convertPlatformName (platformName) { +function convertPlatformName(platformName) { if (platformName === 'darwin') { return 'MacOS' } else if (platformName === 'win32') { @@ -34,16 +34,16 @@ function convertPlatformName (platformName) { } } -function getSendEventCond () { +function getSendEventCond() { const isDev = process.env.NODE_ENV !== 'production' const isDisable = !ConfigManager.default.get().amaEnabled const isOffline = !window.navigator.onLine return isDev || isDisable || isOffline } -function initAwsMobileAnalytics () { +function initAwsMobileAnalytics() { if (getSendEventCond()) return - AWS.config.credentials.get((err) => { + AWS.config.credentials.get(err => { if (!err) { recordDynamicCustomEvent('APP_STARTED') recordStaticCustomEvent() @@ -51,7 +51,7 @@ function initAwsMobileAnalytics () { }) } -function recordDynamicCustomEvent (type, options = {}) { +function recordDynamicCustomEvent(type, options = {}) { if (getSendEventCond()) return try { mobileAnalyticsClient.recordEvent(type, options) @@ -62,7 +62,7 @@ function recordDynamicCustomEvent (type, options = {}) { } } -function recordStaticCustomEvent () { +function recordStaticCustomEvent() { if (getSendEventCond()) return try { mobileAnalyticsClient.recordEvent('UI_COLOR_THEME', { diff --git a/browser/main/lib/Commander.js b/browser/main/lib/Commander.js index 6eef62ee..de6aa27c 100644 --- a/browser/main/lib/Commander.js +++ b/browser/main/lib/Commander.js @@ -1,25 +1,24 @@ let callees = [] -function bind (name, el) { +function bind(name, el) { callees.push({ name: name, element: el }) } -function release (el) { - callees = callees.filter((callee) => callee.element !== el) +function release(el) { + callees = callees.filter(callee => callee.element !== el) } -function fire (command) { +function fire(command) { console.info('COMMAND >>', command) const splitted = command.split(':') const target = splitted[0] const targetCommand = splitted[1] - const targetCallees = callees - .filter((callee) => callee.name === target) + const targetCallees = callees.filter(callee => callee.name === target) - targetCallees.forEach((callee) => { + targetCallees.forEach(callee => { callee.element.fire(targetCommand) }) } diff --git a/browser/main/lib/ConfigManager.js b/browser/main/lib/ConfigManager.js index b5400256..e02deceb 100644 --- a/browser/main/lib/ConfigManager.js +++ b/browser/main/lib/ConfigManager.js @@ -2,7 +2,8 @@ import _ from 'lodash' import RcParser from 'browser/lib/RcParser' import i18n from 'browser/lib/i18n' import ee from 'browser/main/lib/eventEmitter' -import {chooseTheme, applyTheme} from 'browser/main/lib/ThemeManager' +import { chooseTheme, applyTheme } from 'browser/main/lib/ThemeManager' + const OSX = global.process.platform === 'darwin' const win = global.process.platform === 'win32' const electron = require('electron') @@ -26,12 +27,16 @@ export const DEFAULT_CONFIG = { }, sortTagsBy: 'ALPHABETICAL', // 'ALPHABETICAL', 'COUNTER' listStyle: 'DEFAULT', // 'DEFAULT', 'SMALL' + listDirection: 'ASCENDING', // 'ASCENDING', 'DESCENDING' amaEnabled: true, autoUpdateEnabled: true, hotkey: { toggleMain: OSX ? 'Command + Alt + L' : 'Super + Alt + E', toggleMode: OSX ? 'Command + Alt + M' : 'Ctrl + M', - deleteNote: OSX ? 'Command + Shift + Backspace' : 'Ctrl + Shift + Backspace', + toggleDirection: OSX ? 'Command + Alt + Right' : 'Ctrl + Right', + deleteNote: OSX + ? 'Command + Shift + Backspace' + : 'Ctrl + Shift + Backspace', pasteSmartly: OSX ? 'Command + Shift + V' : 'Ctrl + Shift + V', prettifyMarkdown: OSX ? 'Command + Shift + F' : 'Ctrl + Shift + F', sortLines: OSX ? 'Command + Shift + S' : 'Ctrl + Shift + S', @@ -49,6 +54,7 @@ export const DEFAULT_CONFIG = { scheduleEnd: 360, showCopyNotification: true, disableDirectWrite: false, + showScrollBar: true, defaultNote: 'ALWAYS_ASK', // 'ALWAYS_ASK', 'SNIPPET_NOTE', 'MARKDOWN_NOTE' showMenuBar: false }, @@ -80,7 +86,7 @@ export const DEFAULT_CONFIG = { customMarkdownLintConfig: DEFAULT_MARKDOWN_LINT_CONFIG, prettierConfig: ` { "trailingComma": "es5", - "tabWidth": 4, + "tabWidth": 2, "semi": false, "singleQuote": true }`, @@ -119,7 +125,7 @@ export const DEFAULT_CONFIG = { coloredTags: {} } -function validate (config) { +function validate(config) { if (!_.isObject(config)) return false if (!_.isNumber(config.zoom) || config.zoom < 0) return false if (!_.isBoolean(config.isSideNavFolded)) return false @@ -128,13 +134,17 @@ function validate (config) { return true } -function _save (config) { +function _save(config) { window.localStorage.setItem('config', JSON.stringify(config)) } -function get () { +function get() { const rawStoredConfig = window.localStorage.getItem('config') - const storedConfig = Object.assign({}, DEFAULT_CONFIG, JSON.parse(rawStoredConfig)) + const storedConfig = Object.assign( + {}, + DEFAULT_CONFIG, + JSON.parse(rawStoredConfig) + ) let config = storedConfig try { @@ -148,7 +158,10 @@ function get () { _save(config) } - config.autoUpdateEnabled = electronConfig.get('autoUpdateEnabled', config.autoUpdateEnabled) + config.autoUpdateEnabled = electronConfig.get( + 'autoUpdateEnabled', + config.autoUpdateEnabled + ) if (!isInitialized) { isInitialized = true @@ -160,7 +173,9 @@ function get () { document.head.appendChild(editorTheme) } - const theme = consts.THEMES.find(theme => theme.name === config.editor.theme) + const theme = consts.THEMES.find( + theme => theme.name === config.editor.theme + ) if (theme) { editorTheme.setAttribute('href', theme.path) @@ -172,7 +187,7 @@ function get () { return config } -function set (updates) { +function set(updates) { const currentConfig = get() const arrangedUpdates = updates @@ -180,7 +195,12 @@ function set (updates) { arrangedUpdates.preview.customCSS = DEFAULT_CONFIG.preview.customCSS } - const newConfig = Object.assign({}, DEFAULT_CONFIG, currentConfig, arrangedUpdates) + const newConfig = Object.assign( + {}, + DEFAULT_CONFIG, + currentConfig, + arrangedUpdates + ) if (!validate(newConfig)) throw new Error('INVALID CONFIG') _save(newConfig) @@ -197,7 +217,9 @@ function set (updates) { document.head.appendChild(editorTheme) } - const newTheme = consts.THEMES.find(theme => theme.name === newConfig.editor.theme) + const newTheme = consts.THEMES.find( + theme => theme.name === newConfig.editor.theme + ) if (newTheme) { editorTheme.setAttribute('href', newTheme.path) @@ -211,20 +233,45 @@ function set (updates) { ee.emit('config-renew') } -function assignConfigValues (originalConfig, rcConfig) { +function assignConfigValues(originalConfig, rcConfig) { const config = Object.assign({}, DEFAULT_CONFIG, originalConfig, rcConfig) - config.hotkey = Object.assign({}, DEFAULT_CONFIG.hotkey, originalConfig.hotkey, rcConfig.hotkey) - config.blog = Object.assign({}, DEFAULT_CONFIG.blog, originalConfig.blog, rcConfig.blog) - config.ui = Object.assign({}, DEFAULT_CONFIG.ui, originalConfig.ui, rcConfig.ui) - config.editor = Object.assign({}, DEFAULT_CONFIG.editor, originalConfig.editor, rcConfig.editor) - config.preview = Object.assign({}, DEFAULT_CONFIG.preview, originalConfig.preview, rcConfig.preview) + config.hotkey = Object.assign( + {}, + DEFAULT_CONFIG.hotkey, + originalConfig.hotkey, + rcConfig.hotkey + ) + config.blog = Object.assign( + {}, + DEFAULT_CONFIG.blog, + originalConfig.blog, + rcConfig.blog + ) + config.ui = Object.assign( + {}, + DEFAULT_CONFIG.ui, + originalConfig.ui, + rcConfig.ui + ) + config.editor = Object.assign( + {}, + DEFAULT_CONFIG.editor, + originalConfig.editor, + rcConfig.editor + ) + config.preview = Object.assign( + {}, + DEFAULT_CONFIG.preview, + originalConfig.preview, + rcConfig.preview + ) rewriteHotkey(config) return config } -function rewriteHotkey (config) { +function rewriteHotkey(config) { const keys = [...Object.keys(config.hotkey)] keys.forEach(key => { config.hotkey[key] = config.hotkey[key].replace(/Cmd\s/g, 'Command ') diff --git a/browser/main/lib/ZoomManager.js b/browser/main/lib/ZoomManager.js index a8903ca3..56bc9236 100644 --- a/browser/main/lib/ZoomManager.js +++ b/browser/main/lib/ZoomManager.js @@ -5,20 +5,20 @@ const { remote } = electron _init() -function _init () { +function _init() { setZoom(getZoom(), true) } -function _saveZoom (zoomFactor) { - ConfigManager.set({zoom: zoomFactor}) +function _saveZoom(zoomFactor) { + ConfigManager.set({ zoom: zoomFactor }) } -function setZoom (zoomFactor, noSave = false) { +function setZoom(zoomFactor, noSave = false) { if (!noSave) _saveZoom(zoomFactor) remote.getCurrentWebContents().setZoomFactor(zoomFactor) } -function getZoom () { +function getZoom() { const config = ConfigManager.get() return config.zoom diff --git a/browser/main/lib/dataApi/addStorage.js b/browser/main/lib/dataApi/addStorage.js index bfd6698a..370a07e0 100644 --- a/browser/main/lib/dataApi/addStorage.js +++ b/browser/main/lib/dataApi/addStorage.js @@ -16,7 +16,7 @@ const CSON = require('@rokt33r/season') * 3. fetch notes & folders * 4. return `{storage: {...} folders: [folder]}` */ -function addStorage (input) { +function addStorage(input) { if (!_.isString(input.path)) { return Promise.reject(new Error('Path must be a string.')) } @@ -29,7 +29,7 @@ function addStorage (input) { rawStorages = [] } let key = keygen() - while (rawStorages.some((storage) => storage.key === key)) { + while (rawStorages.some(storage => storage.key === key)) { key = keygen() } @@ -43,7 +43,7 @@ function addStorage (input) { return Promise.resolve(newStorage) .then(resolveStorageData) - .then(function saveMetadataToLocalStorage (resolvedStorage) { + .then(function saveMetadataToLocalStorage(resolvedStorage) { newStorage = resolvedStorage rawStorages.push({ key: newStorage.key, @@ -56,27 +56,29 @@ function addStorage (input) { localStorage.setItem('storages', JSON.stringify(rawStorages)) return newStorage }) - .then(function (storage) { - return resolveStorageNotes(storage) - .then((notes) => { - let unknownCount = 0 - notes.forEach((note) => { - if (!storage.folders.some((folder) => note.folder === folder.key)) { - unknownCount++ - storage.folders.push({ - key: note.folder, - color: consts.FOLDER_COLORS[(unknownCount - 1) % 7], - name: 'Unknown ' + unknownCount - }) - } - }) - if (unknownCount > 0) { - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) + .then(function(storage) { + return resolveStorageNotes(storage).then(notes => { + let unknownCount = 0 + notes.forEach(note => { + if (!storage.folders.some(folder => note.folder === folder.key)) { + unknownCount++ + storage.folders.push({ + key: note.folder, + color: consts.FOLDER_COLORS[(unknownCount - 1) % 7], + name: 'Unknown ' + unknownCount + }) } - return notes }) + if (unknownCount > 0) { + CSON.writeFileSync( + path.join(storage.path, 'boostnote.json'), + _.pick(storage, ['folders', 'version']) + ) + } + return notes + }) }) - .then(function returnValue (notes) { + .then(function returnValue(notes) { return { storage: newStorage, notes diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index 971ae812..f3b11997 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -12,14 +12,15 @@ import { isString } from 'lodash' const STORAGE_FOLDER_PLACEHOLDER = ':storage' const DESTINATION_FOLDER = 'attachments' -const PATH_SEPARATORS = escapeStringRegexp(path.posix.sep) + escapeStringRegexp(path.win32.sep) +const PATH_SEPARATORS = + escapeStringRegexp(path.posix.sep) + escapeStringRegexp(path.win32.sep) /** * @description * Create a Image element to get the real size of image. * @param {File} file the File object dropped. * @returns {Promise} Image element created */ -function getImage (file) { +function getImage(file) { if (isString(file)) { return new Promise(resolve => { const img = new Image() @@ -55,38 +56,39 @@ function getImage (file) { * @param {File} file the File object dropped. * @returns {Promise} Orientation info */ -function getOrientation (file) { +function getOrientation(file) { const getData = arrayBuffer => { const view = new DataView(arrayBuffer) // Not start with SOI(Start of image) Marker return fail value - if (view.getUint16(0, false) !== 0xFFD8) return -2 + if (view.getUint16(0, false) !== 0xffd8) return -2 const length = view.byteLength let offset = 2 while (offset < length) { const marker = view.getUint16(offset, false) offset += 2 // Loop and seed for APP1 Marker - if (marker === 0xFFE1) { + if (marker === 0xffe1) { // return fail value if it isn't EXIF data - if (view.getUint32(offset += 2, false) !== 0x45786966) { + if (view.getUint32((offset += 2), false) !== 0x45786966) { return -1 } // Read TIFF header, // First 2bytes defines byte align of TIFF data. // If it is 0x4949="II", it means "Intel" type byte align. // If it is 0x4d4d="MM", it means "Motorola" type byte align - const little = view.getUint16(offset += 6, false) === 0x4949 + const little = view.getUint16((offset += 6), false) === 0x4949 offset += view.getUint32(offset + 4, little) const tags = view.getUint16(offset, little) // Get TAG number offset += 2 for (let i = 0; i < tags; i++) { // Loop to find Orientation TAG and return the value - if (view.getUint16(offset + (i * 12), little) === 0x0112) { - return view.getUint16(offset + (i * 12) + 8, little) + if (view.getUint16(offset + i * 12, little) === 0x0112) { + return view.getUint16(offset + i * 12 + 8, little) } } - } else if ((marker & 0xFF00) !== 0xFF00) { // If not start with 0xFF, not a Marker. + } else if ((marker & 0xff00) !== 0xff00) { + // If not start with 0xFF, not a Marker. break } else { offset += view.getUint16(offset, false) @@ -94,7 +96,7 @@ function getOrientation (file) { } return -1 } - return new Promise((resolve) => { + return new Promise(resolve => { const reader = new FileReader() reader.onload = event => resolve(getData(event.target.result)) reader.readAsArrayBuffer(file.slice(0, 64 * 1024)) @@ -107,31 +109,47 @@ function getOrientation (file) { * @param {*} file the File object dropped. * @return {String} Base64 encoded image. */ -function fixRotate (file) { - return Promise.all([getImage(file), getOrientation(file)]) - .then(([img, orientation]) => { - const canvas = document.createElement('canvas') - const ctx = canvas.getContext('2d') - if (orientation > 4 && orientation < 9) { - canvas.width = img.height - canvas.height = img.width - } else { - canvas.width = img.width - canvas.height = img.height +function fixRotate(file) { + return Promise.all([getImage(file), getOrientation(file)]).then( + ([img, orientation]) => { + const canvas = document.createElement('canvas') + const ctx = canvas.getContext('2d') + if (orientation > 4 && orientation < 9) { + canvas.width = img.height + canvas.height = img.width + } else { + canvas.width = img.width + canvas.height = img.height + } + switch (orientation) { + case 2: + ctx.transform(-1, 0, 0, 1, img.width, 0) + break + case 3: + ctx.transform(-1, 0, 0, -1, img.width, img.height) + break + case 4: + ctx.transform(1, 0, 0, -1, 0, img.height) + break + case 5: + ctx.transform(0, 1, 1, 0, 0, 0) + break + case 6: + ctx.transform(0, 1, -1, 0, img.height, 0) + break + case 7: + ctx.transform(0, -1, -1, 0, img.height, img.width) + break + case 8: + ctx.transform(0, -1, 1, 0, 0, img.width) + break + default: + break + } + ctx.drawImage(img, 0, 0) + return canvas.toDataURL() } - switch (orientation) { - case 2: ctx.transform(-1, 0, 0, 1, img.width, 0); break - case 3: ctx.transform(-1, 0, 0, -1, img.width, img.height); break - case 4: ctx.transform(1, 0, 0, -1, 0, img.height); break - case 5: ctx.transform(0, 1, 1, 0, 0, 0); break - case 6: ctx.transform(0, 1, -1, 0, img.height, 0); break - case 7: ctx.transform(0, -1, -1, 0, img.height, img.width); break - case 8: ctx.transform(0, -1, 1, 0, 0, img.width); break - default: break - } - ctx.drawImage(img, 0, 0) - return canvas.toDataURL() - }) + ) } /** @@ -145,7 +163,12 @@ function fixRotate (file) { * @param {boolean} useRandomName determines whether a random filename for the new file is used. If false the source file name is used * @return {Promise} name (inclusive extension) of the generated file */ -function copyAttachment (sourceFilePath, storageKey, noteKey, useRandomName = true) { +function copyAttachment( + sourceFilePath, + storageKey, + noteKey, + useRandomName = true +) { return new Promise((resolve, reject) => { if (!sourceFilePath) { reject('sourceFilePath has to be given') @@ -160,28 +183,41 @@ function copyAttachment (sourceFilePath, storageKey, noteKey, useRandomName = tr } try { - const isBase64 = typeof sourceFilePath === 'object' && sourceFilePath.type === 'base64' + const isBase64 = + typeof sourceFilePath === 'object' && sourceFilePath.type === 'base64' if (!isBase64 && !fs.existsSync(sourceFilePath)) { return reject('source file does not exist') } const sourcePath = sourceFilePath.sourceFilePath || sourceFilePath - const sourceURL = url.parse(/^\w+:\/\//.test(sourcePath) ? sourcePath : 'file:///' + sourcePath) + const sourceURL = url.parse( + /^\w+:\/\//.test(sourcePath) ? sourcePath : 'file:///' + sourcePath + ) let destinationName if (useRandomName) { - destinationName = `${uniqueSlug()}${path.extname(sourceURL.pathname) || '.png'}` + destinationName = `${uniqueSlug()}${path.extname(sourceURL.pathname) || + '.png'}` } else { destinationName = path.basename(sourceURL.pathname) } const targetStorage = findStorage.findStorage(storageKey) - const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey) + const destinationDir = path.join( + targetStorage.path, + DESTINATION_FOLDER, + noteKey + ) createAttachmentDestinationFolder(targetStorage.path, noteKey) - const outputFile = fs.createWriteStream(path.join(destinationDir, destinationName)) + const outputFile = fs.createWriteStream( + path.join(destinationDir, destinationName) + ) if (isBase64) { - const base64Data = sourceFilePath.data.replace(/^data:image\/\w+;base64,/, '') + const base64Data = sourceFilePath.data.replace( + /^data:image\/\w+;base64,/, + '' + ) const dataBuffer = Buffer.from(base64Data, 'base64') outputFile.write(dataBuffer, () => { resolve(destinationName) @@ -199,12 +235,16 @@ function copyAttachment (sourceFilePath, storageKey, noteKey, useRandomName = tr }) } -function createAttachmentDestinationFolder (destinationStoragePath, noteKey) { +function createAttachmentDestinationFolder(destinationStoragePath, noteKey) { let destinationDir = path.join(destinationStoragePath, DESTINATION_FOLDER) if (!fs.existsSync(destinationDir)) { fs.mkdirSync(destinationDir) } - destinationDir = path.join(destinationStoragePath, DESTINATION_FOLDER, noteKey) + destinationDir = path.join( + destinationStoragePath, + DESTINATION_FOLDER, + noteKey + ) if (!fs.existsSync(destinationDir)) { fs.mkdirSync(destinationDir) } @@ -216,17 +256,28 @@ function createAttachmentDestinationFolder (destinationStoragePath, noteKey) { * @param storagePath Storage path of the current note * @param noteKey Key of the current note */ -function migrateAttachments (markdownContent, storagePath, noteKey) { - if (noteKey !== undefined && sander.existsSync(path.join(storagePath, 'images'))) { +function migrateAttachments(markdownContent, storagePath, noteKey) { + if ( + noteKey !== undefined && + sander.existsSync(path.join(storagePath, 'images')) + ) { const attachments = getAttachmentsInMarkdownContent(markdownContent) || [] if (attachments.length) { createAttachmentDestinationFolder(storagePath, noteKey) } for (const attachment of attachments) { const attachmentBaseName = path.basename(attachment) - const possibleLegacyPath = path.join(storagePath, 'images', attachmentBaseName) + const possibleLegacyPath = path.join( + storagePath, + 'images', + attachmentBaseName + ) if (sander.existsSync(possibleLegacyPath)) { - const destinationPath = path.join(storagePath, DESTINATION_FOLDER, attachmentBaseName) + const destinationPath = path.join( + storagePath, + DESTINATION_FOLDER, + attachmentBaseName + ) if (!sander.existsSync(destinationPath)) { sander.copyFileSync(possibleLegacyPath).to(destinationPath) } @@ -241,10 +292,11 @@ function migrateAttachments (markdownContent, storagePath, noteKey) { * @param {String} storagePath Path of the current storage * @returns {String} postprocessed HTML in which all :storage references are mapped to the actual paths. */ -function fixLocalURLS (renderedHTML, storagePath) { +function fixLocalURLS(renderedHTML, storagePath) { const encodedWin32SeparatorRegex = /%5C/g const storageRegex = new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER, 'g') - const storageUrl = 'file:///' + path.join(storagePath, DESTINATION_FOLDER).replace(/\\/g, '/') + const storageUrl = + 'file:///' + path.join(storagePath, DESTINATION_FOLDER).replace(/\\/g, '/') /* A :storage reference is like `:storage/3b6f8bd6-4edd-4b15-96e0-eadc4475b564/f939b2c3.jpg`. @@ -254,9 +306,17 @@ function fixLocalURLS (renderedHTML, storagePath) { - `(?:\\\/|%5C)[-.\\w]+` will either match `/3b6f8bd6-4edd-4b15-96e0-eadc4475b564` or `/f939b2c3.jpg` - `(?:\\\/|%5C)` match the path seperator. `\\\/` for posix systems and `%5C` for windows. */ - return renderedHTML.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '(?:(?:\\\/|%5C)[-.\\w]+)+', 'g'), function (match) { - return match.replace(encodedWin32SeparatorRegex, '/').replace(storageRegex, storageUrl) - }) + return renderedHTML.replace( + new RegExp( + '/?' + STORAGE_FOLDER_PLACEHOLDER + '(?:(?:\\/|%5C)[-.\\w]+)+', + 'g' + ), + function(match) { + return match + .replace(encodedWin32SeparatorRegex, '/') + .replace(storageRegex, storageUrl) + } + ) } /** @@ -266,7 +326,7 @@ function fixLocalURLS (renderedHTML, storagePath) { * @param {Boolean} showPreview Indicator whether the generated markdown should show a preview of the image. Note that at the moment only previews for images are supported * @returns {String} Generated markdown code */ -function generateAttachmentMarkdown (fileName, path, showPreview) { +function generateAttachmentMarkdown(fileName, path, showPreview) { return `${showPreview ? '!' : ''}[${fileName}](${path})` } @@ -278,53 +338,66 @@ function generateAttachmentMarkdown (fileName, path, showPreview) { * @param {String} noteKey Key of the current note * @param {Event} dropEvent DropEvent */ -function handleAttachmentDrop (codeEditor, storageKey, noteKey, dropEvent) { +function handleAttachmentDrop(codeEditor, storageKey, noteKey, dropEvent) { let promise if (dropEvent.dataTransfer.files.length > 0) { - promise = Promise.all(Array.from(dropEvent.dataTransfer.files).map(file => { - const filePath = file.path - const fileType = file.type // EX) 'image/gif' or 'text/html' - if (fileType.startsWith('image')) { - if (fileType === 'image/gif' || fileType === 'image/svg+xml') { - return copyAttachment(filePath, storageKey, noteKey).then(fileName => ({ - fileName, - title: path.basename(filePath), - isImage: true - })) - } else { - return getOrientation(file) - .then((orientation) => { - if (orientation === -1) { // The image rotation is correct and does not need adjustment - return copyAttachment(filePath, storageKey, noteKey) - } else { - return fixRotate(file).then(data => copyAttachment({ - type: 'base64', - data: data, - sourceFilePath: filePath - }, storageKey, noteKey)) - } - }) - .then(fileName => - ({ + promise = Promise.all( + Array.from(dropEvent.dataTransfer.files).map(file => { + const filePath = file.path + const fileType = file.type // EX) 'image/gif' or 'text/html' + if (fileType.startsWith('image')) { + if (fileType === 'image/gif' || fileType === 'image/svg+xml') { + return copyAttachment(filePath, storageKey, noteKey).then( + fileName => ({ fileName, title: path.basename(filePath), isImage: true }) ) + } else { + return getOrientation(file) + .then(orientation => { + if (orientation === -1) { + // The image rotation is correct and does not need adjustment + return copyAttachment(filePath, storageKey, noteKey) + } else { + return fixRotate(file).then(data => + copyAttachment( + { + type: 'base64', + data: data, + sourceFilePath: filePath + }, + storageKey, + noteKey + ) + ) + } + }) + .then(fileName => ({ + fileName, + title: path.basename(filePath), + isImage: true + })) + } + } else { + return copyAttachment(filePath, storageKey, noteKey).then( + fileName => ({ + fileName, + title: path.basename(filePath), + isImage: false + }) + ) } - } else { - return copyAttachment(filePath, storageKey, noteKey).then(fileName => ({ - fileName, - title: path.basename(filePath), - isImage: false - })) - } - })) + }) + ) } else { let imageURL = dropEvent.dataTransfer.getData('text/plain') if (!imageURL) { - const match = /]*[\s"']src="([^"]+)"/.exec(dropEvent.dataTransfer.getData('text/html')) + const match = /]*[\s"']src="([^"]+)"/.exec( + dropEvent.dataTransfer.getData('text/html') + ) if (match) { imageURL = match[1] } @@ -334,30 +407,43 @@ function handleAttachmentDrop (codeEditor, storageKey, noteKey, dropEvent) { return } - promise = Promise.all([getImage(imageURL) - .then(image => { - const canvas = document.createElement('canvas') - const context = canvas.getContext('2d') - canvas.width = image.width - canvas.height = image.height - context.drawImage(image, 0, 0) + promise = Promise.all([ + getImage(imageURL) + .then(image => { + const canvas = document.createElement('canvas') + const context = canvas.getContext('2d') + canvas.width = image.width + canvas.height = image.height + context.drawImage(image, 0, 0) - return copyAttachment({ - type: 'base64', - data: canvas.toDataURL(), - sourceFilePath: imageURL - }, storageKey, noteKey) - }) - .then(fileName => ({ - fileName, - title: imageURL, - isImage: true - })) + return copyAttachment( + { + type: 'base64', + data: canvas.toDataURL(), + sourceFilePath: imageURL + }, + storageKey, + noteKey + ) + }) + .then(fileName => ({ + fileName, + title: imageURL, + isImage: true + })) ]) } promise.then(files => { - const attachments = files.filter(file => !!file).map(file => generateAttachmentMarkdown(file.title, path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, file.fileName), file.isImage)) + const attachments = files + .filter(file => !!file) + .map(file => + generateAttachmentMarkdown( + file.title, + path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, file.fileName), + file.isImage + ) + ) codeEditor.insertAttachmentMd(attachments.join('\n')) }) @@ -370,7 +456,12 @@ function handleAttachmentDrop (codeEditor, storageKey, noteKey, dropEvent) { * @param {String} noteKey Key of the current note * @param {DataTransferItem} dataTransferItem Part of the past-event */ -function handlePasteImageEvent (codeEditor, storageKey, noteKey, dataTransferItem) { +function handlePasteImageEvent( + codeEditor, + storageKey, + noteKey, + dataTransferItem +) { if (!codeEditor) { throw new Error('codeEditor has to be given') } @@ -389,19 +480,31 @@ function handlePasteImageEvent (codeEditor, storageKey, noteKey, dataTransferIte const reader = new FileReader() let base64data const targetStorage = findStorage.findStorage(storageKey) - const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey) + const destinationDir = path.join( + targetStorage.path, + DESTINATION_FOLDER, + noteKey + ) createAttachmentDestinationFolder(targetStorage.path, noteKey) const imageName = `${uniqueSlug()}.png` const imagePath = path.join(destinationDir, imageName) - reader.onloadend = function () { + reader.onloadend = function() { base64data = reader.result.replace(/^data:image\/png;base64,/, '') base64data += base64data.replace('+', ' ') const binaryData = new Buffer(base64data, 'base64').toString('binary') fs.writeFileSync(imagePath, binaryData, 'binary') - const imageReferencePath = path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, imageName) - const imageMd = generateAttachmentMarkdown(imageName, imageReferencePath, true) + const imageReferencePath = path.join( + STORAGE_FOLDER_PLACEHOLDER, + noteKey, + imageName + ) + const imageMd = generateAttachmentMarkdown( + imageName, + imageReferencePath, + true + ) codeEditor.insertAttachmentMd(imageMd) } reader.readAsDataURL(blob) @@ -414,7 +517,7 @@ function handlePasteImageEvent (codeEditor, storageKey, noteKey, dataTransferIte * @param {String} noteKey Key of the current note * @param {NativeImage} image The native image */ -function handlePasteNativeImage (codeEditor, storageKey, noteKey, image) { +function handlePasteNativeImage(codeEditor, storageKey, noteKey, image) { if (!codeEditor) { throw new Error('codeEditor has to be given') } @@ -430,7 +533,11 @@ function handlePasteNativeImage (codeEditor, storageKey, noteKey, image) { } const targetStorage = findStorage.findStorage(storageKey) - const destinationDir = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey) + const destinationDir = path.join( + targetStorage.path, + DESTINATION_FOLDER, + noteKey + ) createAttachmentDestinationFolder(targetStorage.path, noteKey) @@ -440,19 +547,42 @@ function handlePasteNativeImage (codeEditor, storageKey, noteKey, image) { const binaryData = image.toPNG() fs.writeFileSync(imagePath, binaryData, 'binary') - const imageReferencePath = path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, imageName) - const imageMd = generateAttachmentMarkdown(imageName, imageReferencePath, true) + const imageReferencePath = path.join( + STORAGE_FOLDER_PLACEHOLDER, + noteKey, + imageName + ) + const imageMd = generateAttachmentMarkdown( + imageName, + imageReferencePath, + true + ) codeEditor.insertAttachmentMd(imageMd) } /** -* @description Returns all attachment paths of the given markdown -* @param {String} markdownContent content in which the attachment paths should be found -* @returns {String[]} Array of the relative paths (starting with :storage) of the attachments of the given markdown -*/ -function getAttachmentsInMarkdownContent (markdownContent) { - const preparedInput = markdownContent.replace(new RegExp('[' + PATH_SEPARATORS + ']', 'g'), path.sep) - const regexp = new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '(' + escapeStringRegexp(path.sep) + ')' + '?([a-zA-Z0-9]|-)*' + '(' + escapeStringRegexp(path.sep) + ')' + '([a-zA-Z0-9]|\\.)+(\\.[a-zA-Z0-9]+)?', 'g') + * @description Returns all attachment paths of the given markdown + * @param {String} markdownContent content in which the attachment paths should be found + * @returns {String[]} Array of the relative paths (starting with :storage) of the attachments of the given markdown + */ +function getAttachmentsInMarkdownContent(markdownContent) { + const preparedInput = markdownContent.replace( + new RegExp('[' + PATH_SEPARATORS + ']', 'g'), + path.sep + ) + const regexp = new RegExp( + '/?' + + STORAGE_FOLDER_PLACEHOLDER + + '(' + + escapeStringRegexp(path.sep) + + ')' + + '?([a-zA-Z0-9]|-)*' + + '(' + + escapeStringRegexp(path.sep) + + ')' + + '([a-zA-Z0-9]|\\.)+(\\.[a-zA-Z0-9]+)?', + 'g' + ) return preparedInput.match(regexp) } @@ -462,11 +592,16 @@ function getAttachmentsInMarkdownContent (markdownContent) { * @param {String} storagePath path of the current storage * @returns {String[]} Absolute paths of the referenced attachments */ -function getAbsolutePathsOfAttachmentsInContent (markdownContent, storagePath) { +function getAbsolutePathsOfAttachmentsInContent(markdownContent, storagePath) { const temp = getAttachmentsInMarkdownContent(markdownContent) || [] const result = [] for (const relativePath of temp) { - result.push(relativePath.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER, 'g'), path.join(storagePath, DESTINATION_FOLDER))) + result.push( + relativePath.replace( + new RegExp(STORAGE_FOLDER_PLACEHOLDER, 'g'), + path.join(storagePath, DESTINATION_FOLDER) + ) + ) } return result } @@ -478,7 +613,7 @@ function getAbsolutePathsOfAttachmentsInContent (markdownContent, storagePath) { * @param {String} storageKey Storage key of the destination storage * @param {String} noteKey Key of the current note. Will be used as subfolder in :storage */ -function importAttachments (markDownContent, filepath, storageKey, noteKey) { +function importAttachments(markDownContent, filepath, storageKey, noteKey) { return new Promise((resolve, reject) => { const nameRegex = /(!\[.*?]\()(.+?\..+?)(\))/g let attachPath = nameRegex.exec(markDownContent) @@ -489,8 +624,12 @@ function importAttachments (markDownContent, filepath, storageKey, noteKey) { while (attachPath) { let attachmentPath = attachPath[groupIndex] attachmentPaths.push(attachmentPath) - attachmentPath = path.isAbsolute(attachmentPath) ? attachmentPath : path.join(path.dirname(filepath), attachmentPath) - promiseArray.push(this.copyAttachment(attachmentPath, storageKey, noteKey)) + attachmentPath = path.isAbsolute(attachmentPath) + ? attachmentPath + : path.join(path.dirname(filepath), attachmentPath) + promiseArray.push( + this.copyAttachment(attachmentPath, storageKey, noteKey) + ) attachPath = nameRegex.exec(markDownContent) } @@ -502,19 +641,23 @@ function importAttachments (markDownContent, filepath, storageKey, noteKey) { for (let j = 0; j < promiseArray.length; j++) { promiseArray[j] - .then((fileName) => { - const newPath = path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, fileName) - markDownContent = markDownContent.replace(attachmentPaths[j], newPath) - }) - .catch((e) => { - console.error('File does not exist in path: ' + attachmentPaths[j]) - }) - .finally(() => { - numResolvedPromises++ - if (numResolvedPromises === promiseArray.length) { - resolve(markDownContent) - } - }) + .then(fileName => { + const newPath = path.join( + STORAGE_FOLDER_PLACEHOLDER, + noteKey, + fileName + ) + markDownContent = markDownContent.replace(attachmentPaths[j], newPath) + }) + .catch(e => { + console.error('File does not exist in path: ' + attachmentPaths[j]) + }) + .finally(() => { + numResolvedPromises++ + if (numResolvedPromises === promiseArray.length) { + resolve(markDownContent) + } + }) } }) } @@ -529,7 +672,7 @@ function importAttachments (markDownContent, filepath, storageKey, noteKey) { * @param {String} noteContent Content of the note to be moved * @returns {String} Modified version of noteContent in which the paths of the attachments are fixed */ -function moveAttachments (oldPath, newPath, noteKey, newNoteKey, noteContent) { +function moveAttachments(oldPath, newPath, noteKey, newNoteKey, noteContent) { const src = path.join(oldPath, DESTINATION_FOLDER, noteKey) const dest = path.join(newPath, DESTINATION_FOLDER, newNoteKey) if (fse.existsSync(src)) { @@ -545,10 +688,19 @@ function moveAttachments (oldPath, newPath, noteKey, newNoteKey, noteContent) { * @param newNoteKey note key serving as a replacement * @returns {String} modified note content */ -function replaceNoteKeyWithNewNoteKey (noteContent, oldNoteKey, newNoteKey) { +function replaceNoteKeyWithNewNoteKey(noteContent, oldNoteKey, newNoteKey) { if (noteContent) { - const preparedInput = noteContent.replace(new RegExp('[' + PATH_SEPARATORS + ']', 'g'), path.sep) - return preparedInput.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + oldNoteKey, 'g'), path.join(STORAGE_FOLDER_PLACEHOLDER, newNoteKey)) + const preparedInput = noteContent.replace( + new RegExp('[' + PATH_SEPARATORS + ']', 'g'), + path.sep + ) + return preparedInput.replace( + new RegExp( + STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + oldNoteKey, + 'g' + ), + path.join(STORAGE_FOLDER_PLACEHOLDER, newNoteKey) + ) } return noteContent } @@ -559,15 +711,28 @@ function replaceNoteKeyWithNewNoteKey (noteContent, oldNoteKey, newNoteKey) { * @param noteKey Key of the current note * @returns {String} Input without the references */ -function removeStorageAndNoteReferences (input, noteKey) { - return input.replace(new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?("|])', 'g'), function (match) { - const temp = match - .replace(new RegExp(mdurl.encode(path.win32.sep), 'g'), path.sep) - .replace(new RegExp(mdurl.encode(path.posix.sep), 'g'), path.sep) - .replace(new RegExp(escapeStringRegexp(path.win32.sep), 'g'), path.sep) - .replace(new RegExp(escapeStringRegexp(path.posix.sep), 'g'), path.sep) - return temp.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + '(' + escapeStringRegexp(path.sep) + noteKey + ')?', 'g'), DESTINATION_FOLDER) - }) +function removeStorageAndNoteReferences(input, noteKey) { + return input.replace( + new RegExp('/?' + STORAGE_FOLDER_PLACEHOLDER + '.*?("|])', 'g'), + function(match) { + const temp = match + .replace(new RegExp(mdurl.encode(path.win32.sep), 'g'), path.sep) + .replace(new RegExp(mdurl.encode(path.posix.sep), 'g'), path.sep) + .replace(new RegExp(escapeStringRegexp(path.win32.sep), 'g'), path.sep) + .replace(new RegExp(escapeStringRegexp(path.posix.sep), 'g'), path.sep) + return temp.replace( + new RegExp( + STORAGE_FOLDER_PLACEHOLDER + + '(' + + escapeStringRegexp(path.sep) + + noteKey + + ')?', + 'g' + ), + DESTINATION_FOLDER + ) + } + ) } /** @@ -575,9 +740,13 @@ function removeStorageAndNoteReferences (input, noteKey) { * @param storageKey Key of the storage of the note to be deleted * @param noteKey Key of the note to be deleted */ -function deleteAttachmentFolder (storageKey, noteKey) { +function deleteAttachmentFolder(storageKey, noteKey) { const storagePath = findStorage.findStorage(storageKey) - const noteAttachmentPath = path.join(storagePath.path, DESTINATION_FOLDER, noteKey) + const noteAttachmentPath = path.join( + storagePath.path, + DESTINATION_FOLDER, + noteKey + ) sander.rimrafSync(noteAttachmentPath) } @@ -587,36 +756,66 @@ function deleteAttachmentFolder (storageKey, noteKey) { * @param storageKey StorageKey of the current note. Is used to determine the belonging attachment folder. * @param noteKey NoteKey of the current note. Is used to determine the belonging attachment folder. */ -function deleteAttachmentsNotPresentInNote (markdownContent, storageKey, noteKey) { +function deleteAttachmentsNotPresentInNote( + markdownContent, + storageKey, + noteKey +) { if (storageKey == null || noteKey == null || markdownContent == null) { return } const targetStorage = findStorage.findStorage(storageKey) - const attachmentFolder = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey) + const attachmentFolder = path.join( + targetStorage.path, + DESTINATION_FOLDER, + noteKey + ) const attachmentsInNote = getAttachmentsInMarkdownContent(markdownContent) const attachmentsInNoteOnlyFileNames = [] if (attachmentsInNote) { for (let i = 0; i < attachmentsInNote.length; i++) { - attachmentsInNoteOnlyFileNames.push(attachmentsInNote[i].replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + noteKey + escapeStringRegexp(path.sep), 'g'), '')) + attachmentsInNoteOnlyFileNames.push( + attachmentsInNote[i].replace( + new RegExp( + STORAGE_FOLDER_PLACEHOLDER + + escapeStringRegexp(path.sep) + + noteKey + + escapeStringRegexp(path.sep), + 'g' + ), + '' + ) + ) } } if (fs.existsSync(attachmentFolder)) { fs.readdir(attachmentFolder, (err, files) => { if (err) { - console.error('Error reading directory "' + attachmentFolder + '". Error:') + console.error( + 'Error reading directory "' + attachmentFolder + '". Error:' + ) console.error(err) return } files.forEach(file => { if (!attachmentsInNoteOnlyFileNames.includes(file)) { - const absolutePathOfFile = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey, file) - fs.unlink(absolutePathOfFile, (err) => { + const absolutePathOfFile = path.join( + targetStorage.path, + DESTINATION_FOLDER, + noteKey, + file + ) + fs.unlink(absolutePathOfFile, err => { if (err) { console.error('Could not delete "%s"', absolutePathOfFile) console.error(err) return } - console.info('File "' + absolutePathOfFile + '" deleted because it was not included in the content of the note') + console.info( + 'File "' + + absolutePathOfFile + + '" deleted because it was not included in the content of the note' + ) }) } }) @@ -632,31 +831,53 @@ function deleteAttachmentsNotPresentInNote (markdownContent, storageKey, noteKey * @param noteKey NoteKey of the currentNote * @return {Promise>} Promise returning the list of attachments with their properties */ -function getAttachmentsPathAndStatus (markdownContent, storageKey, noteKey) { +function getAttachmentsPathAndStatus(markdownContent, storageKey, noteKey) { if (storageKey == null || noteKey == null || markdownContent == null) { return null } const targetStorage = findStorage.findStorage(storageKey) - const attachmentFolder = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey) + const attachmentFolder = path.join( + targetStorage.path, + DESTINATION_FOLDER, + noteKey + ) const attachmentsInNote = getAttachmentsInMarkdownContent(markdownContent) const attachmentsInNoteOnlyFileNames = [] if (attachmentsInNote) { for (let i = 0; i < attachmentsInNote.length; i++) { - attachmentsInNoteOnlyFileNames.push(attachmentsInNote[i].replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER + escapeStringRegexp(path.sep) + noteKey + escapeStringRegexp(path.sep), 'g'), '')) + attachmentsInNoteOnlyFileNames.push( + attachmentsInNote[i].replace( + new RegExp( + STORAGE_FOLDER_PLACEHOLDER + + escapeStringRegexp(path.sep) + + noteKey + + escapeStringRegexp(path.sep), + 'g' + ), + '' + ) + ) } } if (fs.existsSync(attachmentFolder)) { return new Promise((resolve, reject) => { fs.readdir(attachmentFolder, (err, files) => { if (err) { - console.error('Error reading directory "' + attachmentFolder + '". Error:') + console.error( + 'Error reading directory "' + attachmentFolder + '". Error:' + ) console.error(err) reject(err) return } const attachments = [] for (const file of files) { - const absolutePathOfFile = path.join(targetStorage.path, DESTINATION_FOLDER, noteKey, file) + const absolutePathOfFile = path.join( + targetStorage.path, + DESTINATION_FOLDER, + noteKey, + file + ) if (!attachmentsInNoteOnlyFileNames.includes(file)) { attachments.push({ path: absolutePathOfFile, isInUse: false }) } else { @@ -675,11 +896,11 @@ function getAttachmentsPathAndStatus (markdownContent, storageKey, noteKey) { * @description Remove all specified attachment paths * @param attachments attachment paths * @return {Promise} Promise after all attachments are removed */ -function removeAttachmentsByPaths (attachments) { +function removeAttachmentsByPaths(attachments) { const promises = [] for (const attachment of attachments) { const promise = new Promise((resolve, reject) => { - fs.unlink(attachment, (err) => { + fs.unlink(attachment, err => { if (err) { console.error('Could not delete "%s"', attachment) console.error(err) @@ -700,29 +921,54 @@ function removeAttachmentsByPaths (attachments) { * @param oldNote Note that is being cloned * @param newNote Clone of the note */ -function cloneAttachments (oldNote, newNote) { +function cloneAttachments(oldNote, newNote) { if (newNote.type === 'MARKDOWN_NOTE') { const oldStorage = findStorage.findStorage(oldNote.storage) const newStorage = findStorage.findStorage(newNote.storage) - const attachmentsPaths = getAbsolutePathsOfAttachmentsInContent(oldNote.content, oldStorage.path) || [] + const attachmentsPaths = + getAbsolutePathsOfAttachmentsInContent( + oldNote.content, + oldStorage.path + ) || [] - const destinationFolder = path.join(newStorage.path, DESTINATION_FOLDER, newNote.key) + const destinationFolder = path.join( + newStorage.path, + DESTINATION_FOLDER, + newNote.key + ) if (!sander.existsSync(destinationFolder)) { sander.mkdirSync(destinationFolder) } for (const attachment of attachmentsPaths) { - const destination = path.join(newStorage.path, DESTINATION_FOLDER, newNote.key, path.basename(attachment)) + const destination = path.join( + newStorage.path, + DESTINATION_FOLDER, + newNote.key, + path.basename(attachment) + ) sander.copyFileSync(attachment).to(destination) } - newNote.content = replaceNoteKeyWithNewNoteKey(newNote.content, oldNote.key, newNote.key) + newNote.content = replaceNoteKeyWithNewNoteKey( + newNote.content, + oldNote.key, + newNote.key + ) } else { - console.debug('Cloning of the attachment was skipped since it only works for MARKDOWN_NOTEs') + console.debug( + 'Cloning of the attachment was skipped since it only works for MARKDOWN_NOTEs' + ) } } -function generateFileNotFoundMarkdown () { - return '**' + i18n.__('⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠') + '**' +function generateFileNotFoundMarkdown() { + return ( + '**' + + i18n.__( + '⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠' + ) + + '**' + ) } /** @@ -730,9 +976,21 @@ function generateFileNotFoundMarkdown () { * @param text Text that might contain a attachment link * @return {Boolean} Result of the test */ -function isAttachmentLink (text) { +function isAttachmentLink(text) { if (text) { - return text.match(new RegExp('.*\\[.*\\]\\( *' + escapeStringRegexp(STORAGE_FOLDER_PLACEHOLDER) + '[' + PATH_SEPARATORS + ']' + '.*\\).*', 'gi')) != null + return ( + text.match( + new RegExp( + '.*\\[.*\\]\\( *' + + escapeStringRegexp(STORAGE_FOLDER_PLACEHOLDER) + + '[' + + PATH_SEPARATORS + + ']' + + '.*\\).*', + 'gi' + ) + ) != null + ) } return false } @@ -745,38 +1003,72 @@ function isAttachmentLink (text) { * @param linkText Text that was pasted * @return {Promise} Promise returning the modified text */ -function handleAttachmentLinkPaste (storageKey, noteKey, linkText) { +function handleAttachmentLinkPaste(storageKey, noteKey, linkText) { if (storageKey != null && noteKey != null && linkText != null) { const storagePath = findStorage.findStorage(storageKey).path const attachments = getAttachmentsInMarkdownContent(linkText) || [] const replaceInstructions = [] const copies = [] for (const attachment of attachments) { - const absPathOfAttachment = attachment.replace(new RegExp(STORAGE_FOLDER_PLACEHOLDER, 'g'), path.join(storagePath, DESTINATION_FOLDER)) + const absPathOfAttachment = attachment.replace( + new RegExp(STORAGE_FOLDER_PLACEHOLDER, 'g'), + path.join(storagePath, DESTINATION_FOLDER) + ) copies.push( - sander.exists(absPathOfAttachment) - .then((fileExists) => { - if (!fileExists) { - const fileNotFoundRegexp = new RegExp('!?' + escapeStringRegexp('[') + '[\\w|\\d|\\s|\\.]*\\]\\(\\s*' + STORAGE_FOLDER_PLACEHOLDER + '[\\w|\\d|\\-|' + PATH_SEPARATORS + ']*' + escapeStringRegexp(path.basename(absPathOfAttachment)) + escapeStringRegexp(')')) - replaceInstructions.push({regexp: fileNotFoundRegexp, replacement: this.generateFileNotFoundMarkdown()}) - return Promise.resolve() - } - return this.copyAttachment(absPathOfAttachment, storageKey, noteKey) - .then((fileName) => { - const replaceLinkRegExp = new RegExp(escapeStringRegexp('(') + ' *' + STORAGE_FOLDER_PLACEHOLDER + '[\\w|\\d|\\-|' + PATH_SEPARATORS + ']*' + escapeStringRegexp(path.basename(absPathOfAttachment)) + ' *' + escapeStringRegexp(')')) - replaceInstructions.push({ - regexp: replaceLinkRegExp, - replacement: '(' + path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, fileName) + ')' - }) - return Promise.resolve() - }) + sander.exists(absPathOfAttachment).then(fileExists => { + if (!fileExists) { + const fileNotFoundRegexp = new RegExp( + '!?' + + escapeStringRegexp('[') + + '[\\w|\\d|\\s|\\.]*\\]\\(\\s*' + + STORAGE_FOLDER_PLACEHOLDER + + '[\\w|\\d|\\-|' + + PATH_SEPARATORS + + ']*' + + escapeStringRegexp(path.basename(absPathOfAttachment)) + + escapeStringRegexp(')') + ) + replaceInstructions.push({ + regexp: fileNotFoundRegexp, + replacement: this.generateFileNotFoundMarkdown() + }) + return Promise.resolve() + } + return this.copyAttachment( + absPathOfAttachment, + storageKey, + noteKey + ).then(fileName => { + const replaceLinkRegExp = new RegExp( + escapeStringRegexp('(') + + ' *' + + STORAGE_FOLDER_PLACEHOLDER + + '[\\w|\\d|\\-|' + + PATH_SEPARATORS + + ']*' + + escapeStringRegexp(path.basename(absPathOfAttachment)) + + ' *' + + escapeStringRegexp(')') + ) + replaceInstructions.push({ + regexp: replaceLinkRegExp, + replacement: + '(' + + path.join(STORAGE_FOLDER_PLACEHOLDER, noteKey, fileName) + + ')' + }) + return Promise.resolve() }) + }) ) } return Promise.all(copies).then(() => { let modifiedLinkText = linkText for (const replaceInstruction of replaceInstructions) { - modifiedLinkText = modifiedLinkText.replace(replaceInstruction.regexp, replaceInstruction.replacement) + modifiedLinkText = modifiedLinkText.replace( + replaceInstruction.regexp, + replaceInstruction.replacement + ) } return modifiedLinkText }) diff --git a/browser/main/lib/dataApi/copyFile.js b/browser/main/lib/dataApi/copyFile.js index 6f23aae2..0b390b2a 100755 --- a/browser/main/lib/dataApi/copyFile.js +++ b/browser/main/lib/dataApi/copyFile.js @@ -7,7 +7,7 @@ const path = require('path') * @param {String} dstPath * @return {Promise} an image path */ -function copyFile (srcPath, dstPath) { +function copyFile(srcPath, dstPath) { if (!path.extname(dstPath)) { dstPath = path.join(dstPath, path.basename(srcPath)) } diff --git a/browser/main/lib/dataApi/createFolder.js b/browser/main/lib/dataApi/createFolder.js index 05fcea37..786d905b 100644 --- a/browser/main/lib/dataApi/createFolder.js +++ b/browser/main/lib/dataApi/createFolder.js @@ -22,7 +22,7 @@ const { findStorage } = require('browser/lib/findStorage') * } * ``` */ -function createFolder (storageKey, input) { +function createFolder(storageKey, input) { let targetStorage try { if (input == null) throw new Error('No input found.') @@ -34,26 +34,28 @@ function createFolder (storageKey, input) { return Promise.reject(e) } - return resolveStorageData(targetStorage) - .then(function createFolder (storage) { - let key = keygen() - while (storage.folders.some((folder) => folder.key === key)) { - key = keygen() - } - const newFolder = { - key, - color: input.color, - name: input.name - } + return resolveStorageData(targetStorage).then(function createFolder(storage) { + let key = keygen() + while (storage.folders.some(folder => folder.key === key)) { + key = keygen() + } + const newFolder = { + key, + color: input.color, + name: input.name + } - storage.folders.push(newFolder) + storage.folders.push(newFolder) - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) + CSON.writeFileSync( + path.join(storage.path, 'boostnote.json'), + _.pick(storage, ['folders', 'version']) + ) - return { - storage - } - }) + return { + storage + } + }) } module.exports = createFolder diff --git a/browser/main/lib/dataApi/createNote.js b/browser/main/lib/dataApi/createNote.js index 5bfa2457..079a988b 100644 --- a/browser/main/lib/dataApi/createNote.js +++ b/browser/main/lib/dataApi/createNote.js @@ -6,9 +6,11 @@ const path = require('path') const CSON = require('@rokt33r/season') const { findStorage } = require('browser/lib/findStorage') -function validateInput (input) { +function validateInput(input) { if (!_.isArray(input.tags)) input.tags = [] - input.tags = input.tags.filter((tag) => _.isString(tag) && tag.trim().length > 0) + input.tags = input.tags.filter( + tag => _.isString(tag) && tag.trim().length > 0 + ) if (!_.isString(input.title)) input.title = '' input.isStarred = !!input.isStarred input.isTrashed = !!input.isTrashed @@ -21,20 +23,24 @@ function validateInput (input) { case 'SNIPPET_NOTE': if (!_.isString(input.description)) input.description = '' if (!_.isArray(input.snippets)) { - input.snippets = [{ - name: '', - mode: 'text', - content: '', - linesHighlighted: [] - }] + input.snippets = [ + { + name: '', + mode: 'text', + content: '', + linesHighlighted: [] + } + ] } break default: - throw new Error('Invalid type: only MARKDOWN_NOTE and SNIPPET_NOTE are available.') + throw new Error( + 'Invalid type: only MARKDOWN_NOTE and SNIPPET_NOTE are available.' + ) } } -function createNote (storageKey, input) { +function createNote(storageKey, input) { let targetStorage try { if (input == null) throw new Error('No input found.') @@ -47,13 +53,13 @@ function createNote (storageKey, input) { } return resolveStorageData(targetStorage) - .then(function checkFolderExists (storage) { - if (_.find(storage.folders, {key: input.folder}) == null) { - throw new Error('Target folder doesn\'t exist.') + .then(function checkFolderExists(storage) { + if (_.find(storage.folders, { key: input.folder }) == null) { + throw new Error("Target folder doesn't exist.") } return storage }) - .then(function saveNote (storage) { + .then(function saveNote(storage) { let key = keygen(true) let isUnique = false while (!isUnique) { @@ -68,7 +74,8 @@ function createNote (storageKey, input) { } } } - const noteData = Object.assign({}, + const noteData = Object.assign( + {}, { createdAt: new Date(), updatedAt: new Date() @@ -77,9 +84,13 @@ function createNote (storageKey, input) { { key, storage: storageKey - }) + } + ) - CSON.writeFileSync(path.join(storage.path, 'notes', key + '.cson'), _.omit(noteData, ['key', 'storage'])) + CSON.writeFileSync( + path.join(storage.path, 'notes', key + '.cson'), + _.omit(noteData, ['key', 'storage']) + ) return noteData }) diff --git a/browser/main/lib/dataApi/createNoteFromUrl.js b/browser/main/lib/dataApi/createNoteFromUrl.js index ead93f9e..2fd3bd9d 100644 --- a/browser/main/lib/dataApi/createNoteFromUrl.js +++ b/browser/main/lib/dataApi/createNoteFromUrl.js @@ -6,8 +6,12 @@ const createNote = require('./createNote') import { push } from 'connected-react-router' import ee from 'browser/main/lib/eventEmitter' -function validateUrl (str) { - if (/^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test(str)) { +function validateUrl(str) { + if ( + /^(?:(?:(?:https?|ftp):)?\/\/)(?:\S+(?::\S*)?@)?(?:(?!(?:10|127)(?:\.\d{1,3}){3})(?!(?:169\.254|192\.168)(?:\.\d{1,3}){2})(?!172\.(?:1[6-9]|2\d|3[0-1])(?:\.\d{1,3}){2})(?:[1-9]\d?|1\d\d|2[01]\d|22[0-3])(?:\.(?:1?\d{1,2}|2[0-4]\d|25[0-5])){2}(?:\.(?:[1-9]\d?|1\d\d|2[0-4]\d|25[0-4]))|(?:(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)(?:\.(?:[a-z\u00a1-\uffff0-9]-*)*[a-z\u00a1-\uffff0-9]+)*(?:\.(?:[a-z\u00a1-\uffff]{2,})).?)(?::\d{2,5})?(?:[/?#]\S*)?$/i.test( + str + ) + ) { return true } else { return false @@ -15,25 +19,33 @@ function validateUrl (str) { } const ERROR_MESSAGES = { - ENOTFOUND: 'URL not found. Please check the URL, or your internet connection and try again.', - VALIDATION_ERROR: 'Please check if the URL follows this format: https://www.google.com', + ENOTFOUND: + 'URL not found. Please check the URL, or your internet connection and try again.', + VALIDATION_ERROR: + 'Please check if the URL follows this format: https://www.google.com', UNEXPECTED: 'Unexpected error! Please check console for details!' } -function createNoteFromUrl (url, storage, folder, dispatch = null, location = null) { +function createNoteFromUrl( + url, + storage, + folder, + dispatch = null, + location = null +) { return new Promise((resolve, reject) => { const td = createTurndownService() if (!validateUrl(url)) { - reject({result: false, error: ERROR_MESSAGES.VALIDATION_ERROR}) + reject({ result: false, error: ERROR_MESSAGES.VALIDATION_ERROR }) } const request = url.startsWith('https') ? https : http - const req = request.request(url, (res) => { + const req = request.request(url, res => { let data = '' - res.on('data', (chunk) => { + res.on('data', chunk => { data += chunk }) @@ -46,20 +58,21 @@ function createNoteFromUrl (url, storage, folder, dispatch = null, location = nu folder: folder, title: '', content: markdownHTML - }) - .then((note) => { + }).then(note => { const noteHash = note.key dispatch({ type: 'UPDATE_NOTE', note: note }) - dispatch(push({ - pathname: location.pathname, - query: {key: noteHash} - })) + dispatch( + push({ + pathname: location.pathname, + query: { key: noteHash } + }) + ) ee.emit('list:jump', noteHash) ee.emit('detail:focus') - resolve({result: true, error: null}) + resolve({ result: true, error: null }) }) } else { createNote(storage, { @@ -67,16 +80,19 @@ function createNoteFromUrl (url, storage, folder, dispatch = null, location = nu folder: folder, title: '', content: markdownHTML - }).then((note) => { - resolve({result: true, note, error: null}) + }).then(note => { + resolve({ result: true, note, error: null }) }) } }) }) - req.on('error', (e) => { + req.on('error', e => { console.error('error in parsing URL', e) - reject({result: false, error: ERROR_MESSAGES[e.code] || ERROR_MESSAGES.UNEXPECTED}) + reject({ + result: false, + error: ERROR_MESSAGES[e.code] || ERROR_MESSAGES.UNEXPECTED + }) }) req.end() diff --git a/browser/main/lib/dataApi/createSnippet.js b/browser/main/lib/dataApi/createSnippet.js index 2e585c9f..48d6705d 100644 --- a/browser/main/lib/dataApi/createSnippet.js +++ b/browser/main/lib/dataApi/createSnippet.js @@ -3,7 +3,7 @@ import crypto from 'crypto' import consts from 'browser/lib/consts' import fetchSnippet from 'browser/main/lib/dataApi/fetchSnippet' -function createSnippet (snippetFile) { +function createSnippet(snippetFile) { return new Promise((resolve, reject) => { const newSnippet = { id: crypto.randomBytes(16).toString('hex'), @@ -12,15 +12,21 @@ function createSnippet (snippetFile) { content: '', linesHighlighted: [] } - fetchSnippet(null, snippetFile).then((snippets) => { - snippets.push(newSnippet) - fs.writeFile(snippetFile || consts.SNIPPET_FILE, JSON.stringify(snippets, null, 4), (err) => { - if (err) reject(err) - resolve(newSnippet) + fetchSnippet(null, snippetFile) + .then(snippets => { + snippets.push(newSnippet) + fs.writeFile( + snippetFile || consts.SNIPPET_FILE, + JSON.stringify(snippets, null, 4), + err => { + if (err) reject(err) + resolve(newSnippet) + } + ) + }) + .catch(err => { + reject(err) }) - }).catch((err) => { - reject(err) - }) }) } diff --git a/browser/main/lib/dataApi/deleteFolder.js b/browser/main/lib/dataApi/deleteFolder.js index 5ccc1414..f20078c8 100644 --- a/browser/main/lib/dataApi/deleteFolder.js +++ b/browser/main/lib/dataApi/deleteFolder.js @@ -18,7 +18,7 @@ const deleteSingleNote = require('./deleteNote') * } * ``` */ -function deleteFolder (storageKey, folderKey) { +function deleteFolder(storageKey, folderKey) { let targetStorage try { targetStorage = findStorage(storageKey) @@ -27,35 +27,36 @@ function deleteFolder (storageKey, folderKey) { } return resolveStorageData(targetStorage) - .then(function assignNotes (storage) { - return resolveStorageNotes(storage) - .then((notes) => { - return { - storage, - notes - } - }) + .then(function assignNotes(storage) { + return resolveStorageNotes(storage).then(notes => { + return { + storage, + notes + } + }) }) - .then(function deleteFolderAndNotes (data) { + .then(function deleteFolderAndNotes(data) { const { storage, notes } = data - storage.folders = storage.folders - .filter(function excludeTargetFolder (folder) { - return folder.key !== folderKey - }) + storage.folders = storage.folders.filter(function excludeTargetFolder( + folder + ) { + return folder.key !== folderKey + }) - const targetNotes = notes.filter(function filterTargetNotes (note) { + const targetNotes = notes.filter(function filterTargetNotes(note) { return note.folder === folderKey }) - const deleteAllNotes = targetNotes - .map(function deleteNote (note) { - return deleteSingleNote(storageKey, note.key) - }) - return Promise.all(deleteAllNotes) - .then(() => storage) + const deleteAllNotes = targetNotes.map(function deleteNote(note) { + return deleteSingleNote(storageKey, note.key) + }) + return Promise.all(deleteAllNotes).then(() => storage) }) - .then(function (storage) { - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) + .then(function(storage) { + CSON.writeFileSync( + path.join(storage.path, 'boostnote.json'), + _.pick(storage, ['folders', 'version']) + ) return { storage, diff --git a/browser/main/lib/dataApi/deleteNote.js b/browser/main/lib/dataApi/deleteNote.js index 46ec2b55..b40fdfe5 100644 --- a/browser/main/lib/dataApi/deleteNote.js +++ b/browser/main/lib/dataApi/deleteNote.js @@ -4,7 +4,7 @@ const sander = require('sander') const attachmentManagement = require('./attachmentManagement') const { findStorage } = require('browser/lib/findStorage') -function deleteNote (storageKey, noteKey) { +function deleteNote(storageKey, noteKey) { let targetStorage try { targetStorage = findStorage(storageKey) @@ -13,7 +13,7 @@ function deleteNote (storageKey, noteKey) { } return resolveStorageData(targetStorage) - .then(function deleteNoteFile (storage) { + .then(function deleteNoteFile(storage) { const notePath = path.join(storage.path, 'notes', noteKey + '.cson') try { @@ -26,8 +26,11 @@ function deleteNote (storageKey, noteKey) { storageKey } }) - .then(function deleteAttachments (storageInfo) { - attachmentManagement.deleteAttachmentFolder(storageInfo.storageKey, storageInfo.noteKey) + .then(function deleteAttachments(storageInfo) { + attachmentManagement.deleteAttachmentFolder( + storageInfo.storageKey, + storageInfo.noteKey + ) return storageInfo }) } diff --git a/browser/main/lib/dataApi/deleteSnippet.js b/browser/main/lib/dataApi/deleteSnippet.js index 0e446886..bd7b1223 100644 --- a/browser/main/lib/dataApi/deleteSnippet.js +++ b/browser/main/lib/dataApi/deleteSnippet.js @@ -2,14 +2,20 @@ import fs from 'fs' import consts from 'browser/lib/consts' import fetchSnippet from 'browser/main/lib/dataApi/fetchSnippet' -function deleteSnippet (snippet, snippetFile) { +function deleteSnippet(snippet, snippetFile) { return new Promise((resolve, reject) => { - fetchSnippet(null, snippetFile).then((snippets) => { - snippets = snippets.filter(currentSnippet => currentSnippet.id !== snippet.id) - fs.writeFile(snippetFile || consts.SNIPPET_FILE, JSON.stringify(snippets, null, 4), (err) => { - if (err) reject(err) - resolve(snippet) - }) + fetchSnippet(null, snippetFile).then(snippets => { + snippets = snippets.filter( + currentSnippet => currentSnippet.id !== snippet.id + ) + fs.writeFile( + snippetFile || consts.SNIPPET_FILE, + JSON.stringify(snippets, null, 4), + err => { + if (err) reject(err) + resolve(snippet) + } + ) }) }) } diff --git a/browser/main/lib/dataApi/exportFolder.js b/browser/main/lib/dataApi/exportFolder.js index 8f15b147..a77ba29b 100644 --- a/browser/main/lib/dataApi/exportFolder.js +++ b/browser/main/lib/dataApi/exportFolder.js @@ -22,7 +22,7 @@ import * as path from 'path' * ``` */ -function exportFolder (storageKey, folderKey, fileType, exportDir) { +function exportFolder(storageKey, folderKey, fileType, exportDir) { let targetStorage try { targetStorage = findStorage(storageKey) @@ -31,24 +31,38 @@ function exportFolder (storageKey, folderKey, fileType, exportDir) { } return resolveStorageData(targetStorage) - .then(function assignNotes (storage) { - return resolveStorageNotes(storage) - .then((notes) => { - return { - storage, - notes - } - }) + .then(function assignNotes(storage) { + return resolveStorageNotes(storage).then(notes => { + return { + storage, + notes + } + }) }) - .then(function exportNotes (data) { + .then(function exportNotes(data) { const { storage, notes } = data - return Promise.all(notes - .filter(note => note.folder === folderKey && note.isTrashed === false && note.type === 'MARKDOWN_NOTE') - .map(note => { - const notePath = path.join(exportDir, `${filenamify(note.title, {replacement: '_'})}.${fileType}`) - return exportNote(note.key, storage.path, note.content, notePath, null) - }) + return Promise.all( + notes + .filter( + note => + note.folder === folderKey && + note.isTrashed === false && + note.type === 'MARKDOWN_NOTE' + ) + .map(note => { + const notePath = path.join( + exportDir, + `${filenamify(note.title, { replacement: '_' })}.${fileType}` + ) + return exportNote( + note.key, + storage.path, + note.content, + notePath, + null + ) + }) ).then(() => ({ storage, folderKey, diff --git a/browser/main/lib/dataApi/exportNote.js b/browser/main/lib/dataApi/exportNote.js index 42e1fa56..ffd45a1c 100755 --- a/browser/main/lib/dataApi/exportNote.js +++ b/browser/main/lib/dataApi/exportNote.js @@ -19,8 +19,16 @@ const attachmentManagement = require('./attachmentManagement') * @param {function} outputFormatter * @return {Promise.<*[]>} */ -function exportNote (nodeKey, storageKey, noteContent, targetPath, outputFormatter) { - const storagePath = path.isAbsolute(storageKey) ? storageKey : findStorage(storageKey).path +function exportNote( + nodeKey, + storageKey, + noteContent, + targetPath, + outputFormatter +) { + const storagePath = path.isAbsolute(storageKey) + ? storageKey + : findStorage(storageKey).path const exportTasks = [] if (!storagePath) { @@ -50,18 +58,19 @@ function exportNote (nodeKey, storageKey, noteContent, targetPath, outputFormatt const tasks = prepareTasks(exportTasks, storagePath, path.dirname(targetPath)) - return Promise.all(tasks.map((task) => copyFile(task.src, task.dst))) - .then(() => exportedData) - .then(data => { - return saveToFile(data, targetPath) - }).catch((err) => { - rollbackExport(tasks) - throw err - }) + return Promise.all(tasks.map(task => copyFile(task.src, task.dst))) + .then(() => exportedData) + .then(data => { + return saveToFile(data, targetPath) + }) + .catch(err => { + rollbackExport(tasks) + throw err + }) } -function prepareTasks (tasks, storagePath, targetPath) { - return tasks.map((task) => { +function prepareTasks(tasks, storagePath, targetPath) { + return tasks.map(task => { if (!path.isAbsolute(task.src)) { task.src = path.join(storagePath, task.src) } @@ -74,9 +83,9 @@ function prepareTasks (tasks, storagePath, targetPath) { }) } -function saveToFile (data, filename) { +function saveToFile(data, filename) { return new Promise((resolve, reject) => { - fs.writeFile(filename, data, (err) => { + fs.writeFile(filename, data, err => { if (err) return reject(err) resolve(filename) @@ -88,9 +97,9 @@ function saveToFile (data, filename) { * Remove exported files * @param tasks Array of copy task objects. Object consists of two mandatory fields – `src` and `dst` */ -function rollbackExport (tasks) { +function rollbackExport(tasks) { const folders = new Set() - tasks.forEach((task) => { + tasks.forEach(task => { let fullpath = task.dst if (!path.extname(task.dst)) { @@ -103,7 +112,7 @@ function rollbackExport (tasks) { } }) - folders.forEach((folder) => { + folders.forEach(folder => { if (fs.readdirSync(folder).length === 0) { fs.rmdir(folder) } diff --git a/browser/main/lib/dataApi/exportStorage.js b/browser/main/lib/dataApi/exportStorage.js index ce2c4573..2a7c725c 100644 --- a/browser/main/lib/dataApi/exportStorage.js +++ b/browser/main/lib/dataApi/exportStorage.js @@ -20,7 +20,7 @@ import * as fs from 'fs' * ``` */ -function exportStorage (storageKey, fileType, exportDir) { +function exportStorage(storageKey, fileType, exportDir) { let targetStorage try { targetStorage = findStorage(storageKey) @@ -29,14 +29,17 @@ function exportStorage (storageKey, fileType, exportDir) { } return resolveStorageData(targetStorage) - .then(storage => ( - resolveStorageNotes(storage).then(notes => ({storage, notes})) - )) - .then(function exportNotes (data) { + .then(storage => + resolveStorageNotes(storage).then(notes => ({ storage, notes })) + ) + .then(function exportNotes(data) { const { storage, notes } = data const folderNamesMapping = {} storage.folders.forEach(folder => { - const folderExportedDir = path.join(exportDir, filenamify(folder.name, {replacement: '_'})) + const folderExportedDir = path.join( + exportDir, + filenamify(folder.name, { replacement: '_' }) + ) folderNamesMapping[folder.key] = folderExportedDir // make sure directory exists try { @@ -47,7 +50,9 @@ function exportStorage (storageKey, fileType, exportDir) { .filter(note => !note.isTrashed && note.type === 'MARKDOWN_NOTE') .forEach(markdownNote => { const folderExportedDir = folderNamesMapping[markdownNote.folder] - const snippetName = `${filenamify(markdownNote.title, {replacement: '_'})}.${fileType}` + const snippetName = `${filenamify(markdownNote.title, { + replacement: '_' + })}.${fileType}` const notePath = path.join(folderExportedDir, snippetName) fs.writeFileSync(notePath, markdownNote.content) }) diff --git a/browser/main/lib/dataApi/fetchSnippet.js b/browser/main/lib/dataApi/fetchSnippet.js index 456a5090..8344eb5d 100644 --- a/browser/main/lib/dataApi/fetchSnippet.js +++ b/browser/main/lib/dataApi/fetchSnippet.js @@ -1,7 +1,7 @@ import fs from 'fs' import consts from 'browser/lib/consts' -function fetchSnippet (id, snippetFile) { +function fetchSnippet(id, snippetFile) { return new Promise((resolve, reject) => { fs.readFile(snippetFile || consts.SNIPPET_FILE, 'utf8', (err, data) => { if (err) { @@ -9,7 +9,9 @@ function fetchSnippet (id, snippetFile) { } const snippets = JSON.parse(data) if (id) { - const snippet = snippets.find(snippet => { return snippet.id === id }) + const snippet = snippets.find(snippet => { + return snippet.id === id + }) resolve(snippet) } resolve(snippets) diff --git a/browser/main/lib/dataApi/init.js b/browser/main/lib/dataApi/init.js index 0dbcc182..0ad35625 100644 --- a/browser/main/lib/dataApi/init.js +++ b/browser/main/lib/dataApi/init.js @@ -21,8 +21,8 @@ const CSON = require('@rokt33r/season') * 3. empty directory */ -function init () { - const fetchStorages = function () { +function init() { + const fetchStorages = function() { let rawStorages try { rawStorages = JSON.parse(window.localStorage.getItem('storages')) @@ -34,44 +34,50 @@ function init () { rawStorages = [] window.localStorage.setItem('storages', JSON.stringify(rawStorages)) } - return Promise.all(rawStorages - .map(resolveStorageData)) + return Promise.all(rawStorages.map(resolveStorageData)) } - const fetchNotes = function (storages) { + const fetchNotes = function(storages) { const findNotesFromEachStorage = storages - .filter(storage => fs.existsSync(storage.path)) - .map((storage) => { - return resolveStorageNotes(storage) - .then((notes) => { - let unknownCount = 0 - notes.forEach((note) => { - if (note && !storage.folders.some((folder) => note.folder === folder.key)) { - unknownCount++ - storage.folders.push({ - key: note.folder, - color: consts.FOLDER_COLORS[(unknownCount - 1) % 7], - name: 'Unknown ' + unknownCount - }) - } - }) - if (unknownCount > 0) { - try { - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) - } catch (e) { - console.log('Error writting boostnote.json: ' + e + ' from init.js') - } + .filter(storage => fs.existsSync(storage.path)) + .map(storage => { + return resolveStorageNotes(storage).then(notes => { + let unknownCount = 0 + notes.forEach(note => { + if ( + note && + !storage.folders.some(folder => note.folder === folder.key) + ) { + unknownCount++ + storage.folders.push({ + key: note.folder, + color: consts.FOLDER_COLORS[(unknownCount - 1) % 7], + name: 'Unknown ' + unknownCount + }) } - return notes }) + if (unknownCount > 0) { + try { + CSON.writeFileSync( + path.join(storage.path, 'boostnote.json'), + _.pick(storage, ['folders', 'version']) + ) + } catch (e) { + console.log( + 'Error writting boostnote.json: ' + e + ' from init.js' + ) + } + } + return notes + }) }) return Promise.all(findNotesFromEachStorage) - .then(function concatNoteGroup (noteGroups) { - return noteGroups.reduce(function (sum, group) { + .then(function concatNoteGroup(noteGroups) { + return noteGroups.reduce(function(sum, group) { return sum.concat(group) }, []) }) - .then(function returnData (notes) { + .then(function returnData(notes) { return { storages, notes @@ -80,12 +86,11 @@ function init () { } return Promise.resolve(fetchStorages()) - .then((storages) => { - return storages - .filter((storage) => { - if (!_.isObject(storage)) return false - return true - }) + .then(storages => { + return storages.filter(storage => { + if (!_.isObject(storage)) return false + return true + }) }) .then(fetchNotes) } diff --git a/browser/main/lib/dataApi/migrateFromV5Storage.js b/browser/main/lib/dataApi/migrateFromV5Storage.js index 78d78746..abf57831 100644 --- a/browser/main/lib/dataApi/migrateFromV5Storage.js +++ b/browser/main/lib/dataApi/migrateFromV5Storage.js @@ -6,102 +6,111 @@ const CSON = require('@rokt33r/season') const path = require('path') const sander = require('sander') -function migrateFromV5Storage (storageKey, data) { +function migrateFromV5Storage(storageKey, data) { let targetStorage try { const cachedStorageList = JSON.parse(localStorage.getItem('storages')) - if (!_.isArray(cachedStorageList)) throw new Error('Target storage doesn\'t exist.') + if (!_.isArray(cachedStorageList)) + throw new Error("Target storage doesn't exist.") - targetStorage = _.find(cachedStorageList, {key: storageKey}) - if (targetStorage == null) throw new Error('Target storage doesn\'t exist.') + targetStorage = _.find(cachedStorageList, { key: storageKey }) + if (targetStorage == null) throw new Error("Target storage doesn't exist.") } catch (e) { return Promise.reject(e) } - return resolveStorageData(targetStorage) - .then(function (storage) { - return importAll(storage, data) - }) + return resolveStorageData(targetStorage).then(function(storage) { + return importAll(storage, data) + }) } -function importAll (storage, data) { +function importAll(storage, data) { const oldArticles = data.articles const notes = [] - data.folders - .forEach(function (oldFolder) { - let folderKey = keygen() - while (storage.folders.some((folder) => folder.key === folderKey)) { - folderKey = keygen() - } - const newFolder = { - key: folderKey, - name: oldFolder.name, - color: consts.FOLDER_COLORS[Math.floor(Math.random() * 7) % 7] - } + data.folders.forEach(function(oldFolder) { + let folderKey = keygen() + while (storage.folders.some(folder => folder.key === folderKey)) { + folderKey = keygen() + } + const newFolder = { + key: folderKey, + name: oldFolder.name, + color: consts.FOLDER_COLORS[Math.floor(Math.random() * 7) % 7] + } - storage.folders.push(newFolder) + storage.folders.push(newFolder) - const articles = oldArticles.filter((article) => article.FolderKey === oldFolder.key) - articles.forEach((article) => { - let noteKey = keygen() - let isUnique = false - while (!isUnique) { - try { - sander.statSync(path.join(storage.path, 'notes', noteKey + '.cson')) - noteKey = keygen() - } catch (err) { - if (err.code === 'ENOENT') { - isUnique = true - } else { - console.error('Failed to read `notes` directory.') - throw err - } + const articles = oldArticles.filter( + article => article.FolderKey === oldFolder.key + ) + articles.forEach(article => { + let noteKey = keygen() + let isUnique = false + while (!isUnique) { + try { + sander.statSync(path.join(storage.path, 'notes', noteKey + '.cson')) + noteKey = keygen() + } catch (err) { + if (err.code === 'ENOENT') { + isUnique = true + } else { + console.error('Failed to read `notes` directory.') + throw err } } + } - if (article.mode === 'markdown') { - const newNote = { - tags: article.tags, - createdAt: article.createdAt, - updatedAt: article.updatedAt, - folder: folderKey, - storage: storage.key, - type: 'MARKDOWN_NOTE', - isStarred: false, - title: article.title, - content: '# ' + article.title + '\n\n' + article.content, - key: noteKey, - linesHighlighted: article.linesHighlighted - } - notes.push(newNote) - } else { - const newNote = { - tags: article.tags, - createdAt: article.createdAt, - updatedAt: article.updatedAt, - folder: folderKey, - storage: storage.key, - type: 'SNIPPET_NOTE', - isStarred: false, - title: article.title, - description: article.title, - key: noteKey, - snippets: [{ + if (article.mode === 'markdown') { + const newNote = { + tags: article.tags, + createdAt: article.createdAt, + updatedAt: article.updatedAt, + folder: folderKey, + storage: storage.key, + type: 'MARKDOWN_NOTE', + isStarred: false, + title: article.title, + content: '# ' + article.title + '\n\n' + article.content, + key: noteKey, + linesHighlighted: article.linesHighlighted + } + notes.push(newNote) + } else { + const newNote = { + tags: article.tags, + createdAt: article.createdAt, + updatedAt: article.updatedAt, + folder: folderKey, + storage: storage.key, + type: 'SNIPPET_NOTE', + isStarred: false, + title: article.title, + description: article.title, + key: noteKey, + snippets: [ + { name: article.mode, mode: article.mode, content: article.content, linesHighlighted: article.linesHighlighted - }] - } - notes.push(newNote) + } + ] } - }) + notes.push(newNote) + } }) - - notes.forEach(function (note) { - CSON.writeFileSync(path.join(storage.path, 'notes', note.key + '.cson'), _.omit(note, ['storage', 'key'])) }) - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['version', 'folders'])) + notes.forEach(function(note) { + CSON.writeFileSync( + path.join(storage.path, 'notes', note.key + '.cson'), + _.omit(note, ['storage', 'key']) + ) + }) + + CSON.writeFileSync( + path.join(storage.path, 'boostnote.json'), + _.pick(storage, ['version', 'folders']) + ) return { storage, diff --git a/browser/main/lib/dataApi/migrateFromV6Storage.js b/browser/main/lib/dataApi/migrateFromV6Storage.js index af4d902f..8255a0de 100644 --- a/browser/main/lib/dataApi/migrateFromV6Storage.js +++ b/browser/main/lib/dataApi/migrateFromV6Storage.js @@ -4,86 +4,91 @@ const keygen = require('browser/lib/keygen') const _ = require('lodash') const CSON = require('@rokt33r/season') -function migrateFromV5Storage (storagePath) { +function migrateFromV5Storage(storagePath) { var boostnoteJSONPath = path.join(storagePath, 'boostnote.json') return Promise.resolve() - .then(function readBoostnoteJSON () { + .then(function readBoostnoteJSON() { return sander.readFile(boostnoteJSONPath, { encoding: 'utf-8' }) }) - .then(function verifyVersion (rawData) { + .then(function verifyVersion(rawData) { var boostnoteJSONData = JSON.parse(rawData) - if (boostnoteJSONData.version === '1.0') throw new Error('Target storage seems to be transformed already.') - if (!_.isArray(boostnoteJSONData.folders)) throw new Error('the value of folders is not an array.') + if (boostnoteJSONData.version === '1.0') + throw new Error('Target storage seems to be transformed already.') + if (!_.isArray(boostnoteJSONData.folders)) + throw new Error('the value of folders is not an array.') return boostnoteJSONData }) - .then(function setVersion (boostnoteJSONData) { + .then(function setVersion(boostnoteJSONData) { boostnoteJSONData.version = '1.0' - return sander.writeFile(boostnoteJSONPath, JSON.stringify(boostnoteJSONData)) + return sander + .writeFile(boostnoteJSONPath, JSON.stringify(boostnoteJSONData)) .then(() => boostnoteJSONData) }) - .then(function fetchNotes (boostnoteJSONData) { - var fetchNotesFromEachFolder = boostnoteJSONData.folders - .map(function (folder) { - const folderDataJSONPath = path.join(storagePath, folder.key, 'data.json') - return sander - .readFile(folderDataJSONPath, { - encoding: 'utf-8' + .then(function fetchNotes(boostnoteJSONData) { + var fetchNotesFromEachFolder = boostnoteJSONData.folders.map(function( + folder + ) { + const folderDataJSONPath = path.join( + storagePath, + folder.key, + 'data.json' + ) + return sander + .readFile(folderDataJSONPath, { + encoding: 'utf-8' + }) + .then(function(rawData) { + var data = JSON.parse(rawData) + if (!_.isArray(data.notes)) + throw new Error('value of notes is not an array.') + return data.notes.map(function setFolderToNote(note) { + note.folder = folder.key + return note }) - .then(function (rawData) { - var data = JSON.parse(rawData) - if (!_.isArray(data.notes)) throw new Error('value of notes is not an array.') - return data.notes - .map(function setFolderToNote (note) { - note.folder = folder.key - return note - }) - }) - .catch(function failedToReadDataJSON (err) { - console.warn('Failed to fetch notes from ', folderDataJSONPath, err) - return [] - }) - }) + }) + .catch(function failedToReadDataJSON(err) { + console.warn('Failed to fetch notes from ', folderDataJSONPath, err) + return [] + }) + }) return Promise.all(fetchNotesFromEachFolder) - .then(function flatten (folderNotes) { - return folderNotes - .reduce(function concatNotes (sum, notes) { - return sum.concat(notes) - }, []) + .then(function flatten(folderNotes) { + return folderNotes.reduce(function concatNotes(sum, notes) { + return sum.concat(notes) + }, []) }) - .then(function saveNotes (notes) { - notes.forEach(function renewKey (note) { + .then(function saveNotes(notes) { + notes.forEach(function renewKey(note) { var newKey = keygen() - while (notes.some((_note) => _note.key === newKey)) { + while (notes.some(_note => _note.key === newKey)) { newKey = keygen() } note.key = newKey }) const noteDirPath = path.join(storagePath, 'notes') - notes - .map(function saveNote (note) { - CSON.writeFileSync(path.join(noteDirPath, note.key) + '.cson', note) - }) + notes.map(function saveNote(note) { + CSON.writeFileSync(path.join(noteDirPath, note.key) + '.cson', note) + }) return true }) - .then(function deleteFolderDir (check) { + .then(function deleteFolderDir(check) { if (check) { - boostnoteJSONData.folders.forEach((folder) => { + boostnoteJSONData.folders.forEach(folder => { sander.rimrafSync(path.join(storagePath, folder.key)) }) } return check }) }) - .catch(function handleError (err) { + .catch(function handleError(err) { console.warn(err) return false }) } module.exports = migrateFromV5Storage - diff --git a/browser/main/lib/dataApi/moveNote.js b/browser/main/lib/dataApi/moveNote.js index c38968cb..b72db7aa 100644 --- a/browser/main/lib/dataApi/moveNote.js +++ b/browser/main/lib/dataApi/moveNote.js @@ -7,90 +7,104 @@ const sander = require('sander') const { findStorage } = require('browser/lib/findStorage') const attachmentManagement = require('./attachmentManagement') -function moveNote (storageKey, noteKey, newStorageKey, newFolderKey) { +function moveNote(storageKey, noteKey, newStorageKey, newFolderKey) { let oldStorage, newStorage try { oldStorage = findStorage(storageKey) newStorage = findStorage(newStorageKey) - if (newStorage == null) throw new Error('Target storage doesn\'t exist.') + if (newStorage == null) throw new Error("Target storage doesn't exist.") } catch (e) { return Promise.reject(e) } - return resolveStorageData(oldStorage) - .then(function saveNote (_oldStorage) { - oldStorage = _oldStorage - let noteData - const notePath = path.join(oldStorage.path, 'notes', noteKey + '.cson') - try { - noteData = CSON.readFileSync(notePath) - } catch (err) { - console.warn('Failed to find note cson', err) - throw err - } - let newNoteKey - return Promise.resolve() - .then(function resolveNewStorage () { - if (storageKey === newStorageKey) { - newNoteKey = noteKey - return oldStorage - } - return resolveStorageData(newStorage) - .then(function findNewNoteKey (_newStorage) { - newStorage = _newStorage - newNoteKey = keygen(true) - let isUnique = false - while (!isUnique) { - try { - sander.statSync(path.join(newStorage.path, 'notes', newNoteKey + '.cson')) - newNoteKey = keygen(true) - } catch (err) { - if (err.code === 'ENOENT') { - isUnique = true - } else { - throw err - } - } - } - - return newStorage - }) - }) - .then(function checkFolderExistsAndPrepareNoteData (newStorage) { - if (_.find(newStorage.folders, {key: newFolderKey}) == null) throw new Error('Target folder doesn\'t exist.') - - noteData.folder = newFolderKey - noteData.key = newNoteKey - noteData.storage = newStorageKey - noteData.updatedAt = new Date() - noteData.oldContent = noteData.content - - return noteData - }) - .then(function moveAttachments (noteData) { - if (oldStorage.path === newStorage.path) { - return noteData - } - - noteData.content = attachmentManagement.moveAttachments(oldStorage.path, newStorage.path, noteKey, newNoteKey, noteData.content) - return noteData - }) - .then(function writeAndReturn (noteData) { - CSON.writeFileSync(path.join(newStorage.path, 'notes', noteData.key + '.cson'), _.omit(noteData, ['key', 'storage', 'oldContent'])) - return noteData - }) - .then(function deleteOldNote (data) { - if (storageKey !== newStorageKey) { + return resolveStorageData(oldStorage).then(function saveNote(_oldStorage) { + oldStorage = _oldStorage + let noteData + const notePath = path.join(oldStorage.path, 'notes', noteKey + '.cson') + try { + noteData = CSON.readFileSync(notePath) + } catch (err) { + console.warn('Failed to find note cson', err) + throw err + } + let newNoteKey + return Promise.resolve() + .then(function resolveNewStorage() { + if (storageKey === newStorageKey) { + newNoteKey = noteKey + return oldStorage + } + return resolveStorageData(newStorage).then(function findNewNoteKey( + _newStorage + ) { + newStorage = _newStorage + newNoteKey = keygen(true) + let isUnique = false + while (!isUnique) { try { - sander.unlinkSync(path.join(oldStorage.path, 'notes', noteKey + '.cson')) + sander.statSync( + path.join(newStorage.path, 'notes', newNoteKey + '.cson') + ) + newNoteKey = keygen(true) } catch (err) { - console.warn(err) + if (err.code === 'ENOENT') { + isUnique = true + } else { + throw err + } } } - return data + return newStorage }) - }) + }) + .then(function checkFolderExistsAndPrepareNoteData(newStorage) { + if (_.find(newStorage.folders, { key: newFolderKey }) == null) + throw new Error("Target folder doesn't exist.") + + noteData.folder = newFolderKey + noteData.key = newNoteKey + noteData.storage = newStorageKey + noteData.updatedAt = new Date() + noteData.oldContent = noteData.content + + return noteData + }) + .then(function moveAttachments(noteData) { + if (oldStorage.path === newStorage.path) { + return noteData + } + + noteData.content = attachmentManagement.moveAttachments( + oldStorage.path, + newStorage.path, + noteKey, + newNoteKey, + noteData.content + ) + return noteData + }) + .then(function writeAndReturn(noteData) { + CSON.writeFileSync( + path.join(newStorage.path, 'notes', noteData.key + '.cson'), + _.omit(noteData, ['key', 'storage', 'oldContent']) + ) + return noteData + }) + .then(function deleteOldNote(data) { + if (storageKey !== newStorageKey) { + try { + sander.unlinkSync( + path.join(oldStorage.path, 'notes', noteKey + '.cson') + ) + } catch (err) { + console.warn(err) + } + } + + return data + }) + }) } module.exports = moveNote diff --git a/browser/main/lib/dataApi/removeStorage.js b/browser/main/lib/dataApi/removeStorage.js index c50bbd12..3f957ab2 100644 --- a/browser/main/lib/dataApi/removeStorage.js +++ b/browser/main/lib/dataApi/removeStorage.js @@ -4,7 +4,7 @@ const _ = require('lodash') * @param {String} key * @return {key} */ -function removeStorage (key) { +function removeStorage(key) { let rawStorages try { @@ -15,10 +15,9 @@ function removeStorage (key) { rawStorages = [] } - rawStorages = rawStorages - .filter(function excludeTargetStorage (rawStorage) { - return rawStorage.key !== key - }) + rawStorages = rawStorages.filter(function excludeTargetStorage(rawStorage) { + return rawStorage.key !== key + }) localStorage.setItem('storages', JSON.stringify(rawStorages)) diff --git a/browser/main/lib/dataApi/renameStorage.js b/browser/main/lib/dataApi/renameStorage.js index 3b806d1c..165a5ab3 100644 --- a/browser/main/lib/dataApi/renameStorage.js +++ b/browser/main/lib/dataApi/renameStorage.js @@ -6,8 +6,9 @@ const resolveStorageData = require('./resolveStorageData') * @param {String} name * @return {Object} Storage meta data */ -function renameStorage (key, name) { - if (!_.isString(name)) return Promise.reject(new Error('Name must be a string.')) +function renameStorage(key, name) { + if (!_.isString(name)) + return Promise.reject(new Error('Name must be a string.')) let cachedStorageList try { @@ -17,7 +18,7 @@ function renameStorage (key, name) { console.error(err) return Promise.reject(err) } - const targetStorage = _.find(cachedStorageList, {key: key}) + const targetStorage = _.find(cachedStorageList, { key: key }) if (targetStorage == null) return Promise.reject('Storage') targetStorage.name = name diff --git a/browser/main/lib/dataApi/reorderFolder.js b/browser/main/lib/dataApi/reorderFolder.js index 9102438e..e86ac838 100644 --- a/browser/main/lib/dataApi/reorderFolder.js +++ b/browser/main/lib/dataApi/reorderFolder.js @@ -17,7 +17,7 @@ const { findStorage } = require('browser/lib/findStorage') * } * ``` */ -function reorderFolder (storageKey, oldIndex, newIndex) { +function reorderFolder(storageKey, oldIndex, newIndex) { let targetStorage try { if (!_.isNumber(oldIndex)) throw new Error('oldIndex must be a number.') @@ -28,15 +28,19 @@ function reorderFolder (storageKey, oldIndex, newIndex) { return Promise.reject(e) } - return resolveStorageData(targetStorage) - .then(function reorderFolder (storage) { - storage.folders = _.move(storage.folders, oldIndex, newIndex) - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) + return resolveStorageData(targetStorage).then(function reorderFolder( + storage + ) { + storage.folders = _.move(storage.folders, oldIndex, newIndex) + CSON.writeFileSync( + path.join(storage.path, 'boostnote.json'), + _.pick(storage, ['folders', 'version']) + ) - return { - storage - } - }) + return { + storage + } + }) } module.exports = reorderFolder diff --git a/browser/main/lib/dataApi/resolveStorageData.js b/browser/main/lib/dataApi/resolveStorageData.js index da41f3d0..3476ec7d 100644 --- a/browser/main/lib/dataApi/resolveStorageData.js +++ b/browser/main/lib/dataApi/resolveStorageData.js @@ -3,7 +3,7 @@ const path = require('path') const CSON = require('@rokt33r/season') const migrateFromV6Storage = require('./migrateFromV6Storage') -function resolveStorageData (storageCache) { +function resolveStorageData(storageCache) { const storage = { key: storageCache.key, name: storageCache.name, @@ -15,13 +15,14 @@ function resolveStorageData (storageCache) { const boostnoteJSONPath = path.join(storageCache.path, 'boostnote.json') try { const jsonData = CSON.readFileSync(boostnoteJSONPath) - if (!_.isArray(jsonData.folders)) throw new Error('folders should be an array.') + if (!_.isArray(jsonData.folders)) + throw new Error('folders should be an array.') storage.folders = jsonData.folders storage.version = jsonData.version } catch (err) { if (err.code === 'ENOENT') { - console.warn('boostnote.json file doesn\'t exist the given path') - CSON.writeFileSync(boostnoteJSONPath, {folders: [], version: '1.0'}) + console.warn("boostnote.json file doesn't exist the given path") + CSON.writeFileSync(boostnoteJSONPath, { folders: [], version: '1.0' }) } else { console.error(err) } @@ -34,8 +35,7 @@ function resolveStorageData (storageCache) { return Promise.resolve(storage) } - return migrateFromV6Storage(storage.path) - .then(() => storage) + return migrateFromV6Storage(storage.path).then(() => storage) } module.exports = resolveStorageData diff --git a/browser/main/lib/dataApi/resolveStorageNotes.js b/browser/main/lib/dataApi/resolveStorageNotes.js index 9da27248..e86ecc26 100644 --- a/browser/main/lib/dataApi/resolveStorageNotes.js +++ b/browser/main/lib/dataApi/resolveStorageNotes.js @@ -2,14 +2,14 @@ const sander = require('sander') const path = require('path') const CSON = require('@rokt33r/season') -function resolveStorageNotes (storage) { +function resolveStorageNotes(storage) { const notesDirPath = path.join(storage.path, 'notes') let notePathList try { notePathList = sander.readdirSync(notesDirPath) } catch (err) { if (err.code === 'ENOENT') { - console.error(notesDirPath, ' doesn\'t exist.') + console.error(notesDirPath, " doesn't exist.") sander.mkdirSync(notesDirPath) } else { console.warn('Failed to find note dir', notesDirPath, err) @@ -17,10 +17,10 @@ function resolveStorageNotes (storage) { notePathList = [] } const notes = notePathList - .filter(function filterOnlyCSONFile (notePath) { + .filter(function filterOnlyCSONFile(notePath) { return /\.cson$/.test(notePath) }) - .map(function parseCSONFile (notePath) { + .map(function parseCSONFile(notePath) { try { const data = CSON.readFileSync(path.join(notesDirPath, notePath)) data.key = path.basename(notePath, '.cson') @@ -30,7 +30,7 @@ function resolveStorageNotes (storage) { console.error(`error on note path: ${notePath}, error: ${err}`) } }) - .filter(function filterOnlyNoteObject (noteObj) { + .filter(function filterOnlyNoteObject(noteObj) { return typeof noteObj === 'object' }) diff --git a/browser/main/lib/dataApi/toggleStorage.js b/browser/main/lib/dataApi/toggleStorage.js index 246d85ef..013c15d4 100644 --- a/browser/main/lib/dataApi/toggleStorage.js +++ b/browser/main/lib/dataApi/toggleStorage.js @@ -6,7 +6,7 @@ const resolveStorageData = require('./resolveStorageData') * @param {Boolean} isOpen * @return {Object} Storage meta data */ -function toggleStorage (key, isOpen) { +function toggleStorage(key, isOpen) { let cachedStorageList try { cachedStorageList = JSON.parse(localStorage.getItem('storages')) @@ -15,7 +15,7 @@ function toggleStorage (key, isOpen) { console.error(err) return Promise.reject(err) } - const targetStorage = _.find(cachedStorageList, {key: key}) + const targetStorage = _.find(cachedStorageList, { key: key }) if (targetStorage == null) return Promise.reject('Storage') targetStorage.isOpen = isOpen diff --git a/browser/main/lib/dataApi/updateFolder.js b/browser/main/lib/dataApi/updateFolder.js index 2a325c60..84658ffc 100644 --- a/browser/main/lib/dataApi/updateFolder.js +++ b/browser/main/lib/dataApi/updateFolder.js @@ -22,7 +22,7 @@ const { findStorage } = require('browser/lib/findStorage') * } * ``` */ -function updateFolder (storageKey, folderKey, input) { +function updateFolder(storageKey, folderKey, input) { let targetStorage try { if (input == null) throw new Error('No input found.') @@ -34,19 +34,21 @@ function updateFolder (storageKey, folderKey, input) { return Promise.reject(e) } - return resolveStorageData(targetStorage) - .then(function updateFolder (storage) { - const targetFolder = _.find(storage.folders, {key: folderKey}) - if (targetFolder == null) throw new Error('Target folder doesn\'t exist.') - targetFolder.name = input.name - targetFolder.color = input.color + return resolveStorageData(targetStorage).then(function updateFolder(storage) { + const targetFolder = _.find(storage.folders, { key: folderKey }) + if (targetFolder == null) throw new Error("Target folder doesn't exist.") + targetFolder.name = input.name + targetFolder.color = input.color - CSON.writeFileSync(path.join(storage.path, 'boostnote.json'), _.pick(storage, ['folders', 'version'])) + CSON.writeFileSync( + path.join(storage.path, 'boostnote.json'), + _.pick(storage, ['folders', 'version']) + ) - return { - storage - } - }) + return { + storage + } + }) } module.exports = updateFolder diff --git a/browser/main/lib/dataApi/updateNote.js b/browser/main/lib/dataApi/updateNote.js index ce9fabcf..775888a6 100644 --- a/browser/main/lib/dataApi/updateNote.js +++ b/browser/main/lib/dataApi/updateNote.js @@ -4,13 +4,14 @@ const path = require('path') const CSON = require('@rokt33r/season') const { findStorage } = require('browser/lib/findStorage') -function validateInput (input) { +function validateInput(input) { const validatedInput = {} if (input.tags != null) { if (!_.isArray(input.tags)) validatedInput.tags = [] - validatedInput.tags = input.tags - .filter((tag) => _.isString(tag) && tag.trim().length > 0) + validatedInput.tags = input.tags.filter( + tag => _.isString(tag) && tag.trim().length > 0 + ) } if (input.title != null) { @@ -40,7 +41,8 @@ function validateInput (input) { if (!_.isString(input.content)) validatedInput.content = '' else validatedInput.content = input.content - if (!_.isArray(input.linesHighlighted)) validatedInput.linesHighlighted = [] + if (!_.isArray(input.linesHighlighted)) + validatedInput.linesHighlighted = [] else validatedInput.linesHighlighted = input.linesHighlighted } return validatedInput @@ -51,30 +53,33 @@ function validateInput (input) { } if (input.snippets != null) { if (!_.isArray(input.snippets)) { - validatedInput.snippets = [{ - name: '', - mode: 'text', - content: '', - linesHighlighted: [] - }] + validatedInput.snippets = [ + { + name: '', + mode: 'text', + content: '', + linesHighlighted: [] + } + ] } else { validatedInput.snippets = input.snippets } - validatedInput.snippets - .filter((snippet) => { - if (!_.isString(snippet.name)) return false - if (!_.isString(snippet.mode)) return false - if (!_.isString(snippet.content)) return false - return true - }) + validatedInput.snippets.filter(snippet => { + if (!_.isString(snippet.name)) return false + if (!_.isString(snippet.mode)) return false + if (!_.isString(snippet.content)) return false + return true + }) } return validatedInput default: - throw new Error('Invalid type: only MARKDOWN_NOTE and SNIPPET_NOTE are available.') + throw new Error( + 'Invalid type: only MARKDOWN_NOTE and SNIPPET_NOTE are available.' + ) } } -function updateNote (storageKey, noteKey, input) { +function updateNote(storageKey, noteKey, input) { let targetStorage try { if (input == null) throw new Error('No input found.') @@ -85,55 +90,61 @@ function updateNote (storageKey, noteKey, input) { return Promise.reject(e) } - return resolveStorageData(targetStorage) - .then(function saveNote (storage) { - let noteData - const notePath = path.join(storage.path, 'notes', noteKey + '.cson') - try { - noteData = CSON.readFileSync(notePath) - } catch (err) { - console.warn('Failed to find note cson', err) - noteData = input.type === 'SNIPPET_NOTE' + return resolveStorageData(targetStorage).then(function saveNote(storage) { + let noteData + const notePath = path.join(storage.path, 'notes', noteKey + '.cson') + try { + noteData = CSON.readFileSync(notePath) + } catch (err) { + console.warn('Failed to find note cson', err) + noteData = + input.type === 'SNIPPET_NOTE' ? { - type: 'SNIPPET_NOTE', - description: [], - snippets: [{ - name: '', - mode: 'text', + type: 'SNIPPET_NOTE', + description: [], + snippets: [ + { + name: '', + mode: 'text', + content: '', + linesHighlighted: [] + } + ] + } + : { + type: 'MARKDOWN_NOTE', content: '', linesHighlighted: [] - }] - } - : { - type: 'MARKDOWN_NOTE', - content: '', - linesHighlighted: [] - } - noteData.title = '' - if (storage.folders.length === 0) throw new Error('Failed to restore note: No folder exists.') - noteData.folder = storage.folders[0].key - noteData.createdAt = new Date() - noteData.updatedAt = new Date() - noteData.isStarred = false - noteData.isTrashed = false - noteData.tags = [] - noteData.isPinned = false - } + } + noteData.title = '' + if (storage.folders.length === 0) + throw new Error('Failed to restore note: No folder exists.') + noteData.folder = storage.folders[0].key + noteData.createdAt = new Date() + noteData.updatedAt = new Date() + noteData.isStarred = false + noteData.isTrashed = false + noteData.tags = [] + noteData.isPinned = false + } - if (noteData.type === 'SNIPPET_NOTE') { - noteData.title - } + if (noteData.type === 'SNIPPET_NOTE') { + noteData.title + } - Object.assign(noteData, input, { - key: noteKey, - updatedAt: new Date(), - storage: storageKey - }) - - CSON.writeFileSync(path.join(storage.path, 'notes', noteKey + '.cson'), _.omit(noteData, ['key', 'storage'])) - - return noteData + Object.assign(noteData, input, { + key: noteKey, + updatedAt: new Date(), + storage: storageKey }) + + CSON.writeFileSync( + path.join(storage.path, 'notes', noteKey + '.cson'), + _.omit(noteData, ['key', 'storage']) + ) + + return noteData + }) } module.exports = updateNote diff --git a/browser/main/lib/dataApi/updateSnippet.js b/browser/main/lib/dataApi/updateSnippet.js index f132d83f..95acf053 100644 --- a/browser/main/lib/dataApi/updateSnippet.js +++ b/browser/main/lib/dataApi/updateSnippet.js @@ -1,9 +1,11 @@ import fs from 'fs' import consts from 'browser/lib/consts' -function updateSnippet (snippet, snippetFile) { +function updateSnippet(snippet, snippetFile) { return new Promise((resolve, reject) => { - const snippets = JSON.parse(fs.readFileSync(snippetFile || consts.SNIPPET_FILE, 'utf-8')) + const snippets = JSON.parse( + fs.readFileSync(snippetFile || consts.SNIPPET_FILE, 'utf-8') + ) for (let i = 0; i < snippets.length; i++) { const currentSnippet = snippets[i] @@ -21,11 +23,15 @@ function updateSnippet (snippet, snippetFile) { currentSnippet.name = snippet.name currentSnippet.prefix = snippet.prefix currentSnippet.content = snippet.content - currentSnippet.linesHighlighted = (snippet.linesHighlighted) - fs.writeFile(snippetFile || consts.SNIPPET_FILE, JSON.stringify(snippets, null, 4), (err) => { - if (err) reject(err) - resolve(snippets) - }) + currentSnippet.linesHighlighted = snippet.linesHighlighted + fs.writeFile( + snippetFile || consts.SNIPPET_FILE, + JSON.stringify(snippets, null, 4), + err => { + if (err) reject(err) + resolve(snippets) + } + ) } } } diff --git a/browser/main/lib/eventEmitter.js b/browser/main/lib/eventEmitter.js index 1276545b..370ea3a9 100644 --- a/browser/main/lib/eventEmitter.js +++ b/browser/main/lib/eventEmitter.js @@ -1,19 +1,19 @@ const electron = require('electron') const { ipcRenderer, remote } = electron -function on (name, listener) { +function on(name, listener) { ipcRenderer.on(name, listener) } -function off (name, listener) { +function off(name, listener) { ipcRenderer.removeListener(name, listener) } -function once (name, listener) { +function once(name, listener) { ipcRenderer.once(name, listener) } -function emit (name, ...args) { +function emit(name, ...args) { remote.getCurrentWindow().webContents.send(name, ...args) } diff --git a/browser/main/lib/ipcClient.js b/browser/main/lib/ipcClient.js index c06296b5..4c25d52c 100644 --- a/browser/main/lib/ipcClient.js +++ b/browser/main/lib/ipcClient.js @@ -12,14 +12,14 @@ nodeIpc.config.silent = true nodeIpc.connectTo( 'node', path.join(app.getPath('userData'), 'boostnote.service'), - function () { - nodeIpc.of.node.on('error', function (err) { + function() { + nodeIpc.of.node.on('error', function(err) { console.error(err) }) - nodeIpc.of.node.on('connect', function () { - ipcRenderer.send('config-renew', {config: ConfigManager.get()}) + nodeIpc.of.node.on('connect', function() { + ipcRenderer.send('config-renew', { config: ConfigManager.get() }) }) - nodeIpc.of.node.on('disconnect', function () { + nodeIpc.of.node.on('disconnect', function() { return }) } diff --git a/browser/main/lib/modal.js b/browser/main/lib/modal.js index 955cb5c8..08d8c7f1 100644 --- a/browser/main/lib/modal.js +++ b/browser/main/lib/modal.js @@ -4,7 +4,7 @@ import ReactDOM from 'react-dom' import { store } from '../store' class ModalBase extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { component: null, @@ -13,20 +13,30 @@ class ModalBase extends React.Component { } } - close () { - if (modalBase != null) modalBase.setState({component: null, componentProps: null, isHidden: true}) + close() { + if (modalBase != null) + modalBase.setState({ + component: null, + componentProps: null, + isHidden: true + }) // Toggle overflow style on NoteList - const list = document.querySelector('.NoteList__list___browser-main-NoteList-') + const list = document.querySelector( + '.NoteList__list___browser-main-NoteList-' + ) list.style.overflow = 'auto' } - render () { + render() { return (
-
this.close(e)} className='modalBack' /> +
this.close(e)} className='modalBack' /> {this.state.component == null ? null : ( - + )}
@@ -38,21 +48,31 @@ const el = document.createElement('div') document.body.appendChild(el) const modalBase = ReactDOM.render(, el) -export function openModal (component, props) { - if (modalBase == null) { return } +export function openModal(component, props) { + if (modalBase == null) { + return + } // Hide scrollbar by removing overflow when modal opens - const list = document.querySelector('.NoteList__list___browser-main-NoteList-') + const list = document.querySelector( + '.NoteList__list___browser-main-NoteList-' + ) list.style.overflow = 'hidden' document.body.setAttribute('data-modal', 'open') - modalBase.setState({component: component, componentProps: props, isHidden: false}) + modalBase.setState({ + component: component, + componentProps: props, + isHidden: false + }) } -export function closeModal () { - if (modalBase == null) { return } +export function closeModal() { + if (modalBase == null) { + return + } modalBase.close() } -export function isModalOpen () { +export function isModalOpen() { return !modalBase.state.isHidden } diff --git a/browser/main/lib/notify.js b/browser/main/lib/notify.js index 458a784d..6054cb36 100644 --- a/browser/main/lib/notify.js +++ b/browser/main/lib/notify.js @@ -1,8 +1,12 @@ const path = require('path') -function notify (title, options) { +function notify(title, options) { if (process.platform === 'win32') { - options.icon = path.join('file://', global.__dirname, '../../resources/app.png') + options.icon = path.join( + 'file://', + global.__dirname, + '../../resources/app.png' + ) options.silent = false } return new window.Notification(title, options) diff --git a/browser/main/lib/shortcut.js b/browser/main/lib/shortcut.js index 3165606a..aab644d7 100644 --- a/browser/main/lib/shortcut.js +++ b/browser/main/lib/shortcut.js @@ -1,13 +1,16 @@ import ee from 'browser/main/lib/eventEmitter' module.exports = { - 'toggleMode': () => { + toggleMode: () => { ee.emit('topbar:togglemodebutton') }, - 'deleteNote': () => { + toggleDirection: () => { + ee.emit('topbar:toggledirectionbutton') + }, + deleteNote: () => { ee.emit('hotkey:deletenote') }, - 'toggleMenuBar': () => { + toggleMenuBar: () => { ee.emit('menubar:togglemenubar') } } diff --git a/browser/main/lib/shortcutManager.js b/browser/main/lib/shortcutManager.js index ac2a3a08..7575bb99 100644 --- a/browser/main/lib/shortcutManager.js +++ b/browser/main/lib/shortcutManager.js @@ -7,7 +7,7 @@ import functions from './shortcut' let shortcuts = CM.get().hotkey -ee.on('config-renew', function () { +ee.on('config-renew', function() { // only update if hotkey changed ! const newHotkey = CM.get().hotkey if (!isObjectEqual(newHotkey, shortcuts)) { @@ -15,17 +15,17 @@ ee.on('config-renew', function () { } }) -function updateShortcut (newHotkey) { +function updateShortcut(newHotkey) { Mousetrap.reset() shortcuts = newHotkey applyShortcuts(newHotkey) } -function formatShortcut (shortcut) { +function formatShortcut(shortcut) { return shortcut.toLowerCase().replace(/ /g, '') } -function applyShortcuts (shortcuts) { +function applyShortcuts(shortcuts) { for (const shortcut in shortcuts) { const toggler = formatShortcut(shortcuts[shortcut]) // only bind if the function for that shortcut exists diff --git a/browser/main/modals/CreateFolderModal.js b/browser/main/modals/CreateFolderModal.js index b48d6e42..26b5a245 100644 --- a/browser/main/modals/CreateFolderModal.js +++ b/browser/main/modals/CreateFolderModal.js @@ -10,7 +10,7 @@ import AwsMobileAnalyticsConfig from 'browser/main/lib/AwsMobileAnalyticsConfig' import i18n from 'browser/lib/i18n' class CreateFolderModal extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -18,39 +18,39 @@ class CreateFolderModal extends React.Component { } } - componentDidMount () { + componentDidMount() { this.refs.name.focus() this.refs.name.select() } - handleCloseButtonClick (e) { + handleCloseButtonClick(e) { this.props.close() } - handleChange (e) { + handleChange(e) { this.setState({ name: this.refs.name.value }) } - handleKeyDown (e) { + handleKeyDown(e) { if (e.keyCode === 27) { this.props.close() } } - handleInputKeyDown (e) { + handleInputKeyDown(e) { switch (e.keyCode) { case 13: this.confirm() } } - handleConfirmButtonClick (e) { + handleConfirmButtonClick(e) { this.confirm() } - confirm () { + confirm() { AwsMobileAnalyticsConfig.recordDynamicCustomEvent('ADD_FOLDER') if (this.state.name.trim().length > 0) { const { storage } = this.props @@ -59,42 +59,48 @@ class CreateFolderModal extends React.Component { color: consts.FOLDER_COLORS[Math.floor(Math.random() * 7) % 7] } - dataApi.createFolder(storage.key, input) - .then((data) => { + dataApi + .createFolder(storage.key, input) + .then(data => { store.dispatch({ type: 'UPDATE_FOLDER', storage: data.storage }) this.props.close() }) - .catch((err) => { + .catch(err => { console.error(err) }) } } - render () { + render() { return ( -
this.handleKeyDown(e)} + onKeyDown={e => this.handleKeyDown(e)} >
{i18n.__('Create new folder')}
- this.handleCloseButtonClick(e)} /> + this.handleCloseButtonClick(e)} + />
{i18n.__('Folder name')}
- this.handleChange(e)} - onKeyDown={(e) => this.handleInputKeyDown(e)} + onChange={e => this.handleChange(e)} + onKeyDown={e => this.handleInputKeyDown(e)} />
- diff --git a/browser/main/modals/CreateFolderModal.styl b/browser/main/modals/CreateFolderModal.styl index 93848683..95d6249a 100644 --- a/browser/main/modals/CreateFolderModal.styl +++ b/browser/main/modals/CreateFolderModal.styl @@ -51,106 +51,40 @@ font-size 14px colorPrimaryButton() -body[data-theme="dark"] - .root - modalDark() - width 500px - height 270px - overflow hidden - position relative +apply-theme(theme) + body[data-theme={theme}] + .root + width 500px + height 270px + overflow hidden + position relative + position relative + z-index $modal-z-index + width 100% + background-color get-theme-var(theme, 'backgroundColor') + overflow hidden + border-radius $modal-border-radius - .header - background-color transparent - border-color $ui-dark-borderColor - color $ui-dark-text-color + .header + background-color transparent + border-color $ui-dark-borderColor + color get-theme-var(theme, 'text-color') - .control-folder-label - color $ui-dark-text-color + .control-folder-label + color get-theme-var(theme, 'text-color') - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white + .control-folder-input + border 1px solid $ui-input--create-folder-modal + color white - .description - color $ui-inactive-text-color + .description + color $ui-inactive-text-color - .control-confirmButton - colorDarkPrimaryButton() + .control-confirmButton + colorThemedPrimaryButton(theme) -body[data-theme="solarized-dark"] - .root - modalSolarizedDark() - width 500px - height 270px - overflow hidden - position relative +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) - .header - background-color transparent - border-color $ui-dark-borderColor - color $ui-solarized-dark-text-color - - .control-folder-label - color $ui-solarized-dark-text-color - - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white - - .description - color $ui-inactive-text-color - - .control-confirmButton - colorSolarizedDarkPrimaryButton() - -body[data-theme="monokai"] - .root - modalMonokai() - width 500px - height 270px - overflow hidden - position relative - - .header - background-color transparent - border-color $ui-dark-borderColor - color $ui-monokai-text-color - - .control-folder-label - color $ui-monokai-text-color - - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white - - .description - color $ui-inactive-text-color - - .control-confirmButton - colorMonokaiPrimaryButton() - -body[data-theme="dracula"] - .root - modalDracula() - width 500px - height 270px - overflow hidden - position relative - - .header - background-color transparent - border-color $ui-dark-borderColor - color $ui-dracula-text-color - - .control-folder-label - color $ui-dracula-text-color - - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white - - .description - color $ui-inactive-text-color - - .control-confirmButton - colorDraculaPrimaryButton() \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/CreateMarkdownFromURLModal.js b/browser/main/modals/CreateMarkdownFromURLModal.js index 31988059..090fe5a4 100644 --- a/browser/main/modals/CreateMarkdownFromURLModal.js +++ b/browser/main/modals/CreateMarkdownFromURLModal.js @@ -7,7 +7,7 @@ import ModalEscButton from 'browser/components/ModalEscButton' import i18n from 'browser/lib/i18n' class CreateMarkdownFromURLModal extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -17,89 +17,101 @@ class CreateMarkdownFromURLModal extends React.Component { } } - componentDidMount () { + componentDidMount() { this.refs.name.focus() this.refs.name.select() } - handleCloseButtonClick (e) { + handleCloseButtonClick(e) { this.props.close() } - handleChange (e) { + handleChange(e) { this.setState({ name: this.refs.name.value }) } - handleKeyDown (e) { + handleKeyDown(e) { if (e.keyCode === 27) { this.props.close() } } - handleInputKeyDown (e) { + handleInputKeyDown(e) { switch (e.keyCode) { case 13: this.confirm() } } - handleConfirmButtonClick (e) { + handleConfirmButtonClick(e) { this.confirm() } - showError (message) { + showError(message) { this.setState({ showerror: true, errormessage: message }) } - hideError () { + hideError() { this.setState({ showerror: false, errormessage: '' }) } - confirm () { + confirm() { this.hideError() const { storage, folder, dispatch, location } = this.props - dataApi.createNoteFromUrl(this.state.name, storage, folder, dispatch, location).then((result) => { - this.props.close() - }).catch((result) => { - this.showError(result.error) - }) + dataApi + .createNoteFromUrl(this.state.name, storage, folder, dispatch, location) + .then(result => { + this.props.close() + }) + .catch(result => { + this.showError(result.error) + }) } - render () { + render() { return ( -
this.handleKeyDown(e)} + onKeyDown={e => this.handleKeyDown(e)} >
{i18n.__('Import Markdown From URL')}
- this.handleCloseButtonClick(e)} /> + this.handleCloseButtonClick(e)} + />
-
{i18n.__('Insert URL Here')}
- + {i18n.__('Insert URL Here')} +
+ this.handleChange(e)} - onKeyDown={(e) => this.handleInputKeyDown(e)} + onChange={e => this.handleChange(e)} + onKeyDown={e => this.handleInputKeyDown(e)} />
- -
{this.state.errormessage}
+
+ {this.state.errormessage} +
) diff --git a/browser/main/modals/CreateMarkdownFromURLModal.styl b/browser/main/modals/CreateMarkdownFromURLModal.styl index 5e59e465..8aca1505 100644 --- a/browser/main/modals/CreateMarkdownFromURLModal.styl +++ b/browser/main/modals/CreateMarkdownFromURLModal.styl @@ -51,110 +51,39 @@ font-size 14px colorPrimaryButton() -body[data-theme="dark"] - .root - modalDark() - width 500px - height 270px - overflow hidden - position relative - - .header - background-color transparent - border-color $ui-dark-borderColor - color $ui-dark-text-color - - .control-folder-label - color $ui-dark-text-color - - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white - - .description - color $ui-inactive-text-color - - .control-confirmButton - colorDarkPrimaryButton() - -body[data-theme="solarized-dark"] - .root - modalSolarizedDark() - width 500px - height 270px - overflow hidden - position relative - - .header - background-color transparent - border-color $ui-dark-borderColor - color $ui-solarized-dark-text-color - - .control-folder-label - color $ui-solarized-dark-text-color - - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white - - .description - color $ui-inactive-text-color - - .control-confirmButton - colorSolarizedDarkPrimaryButton() - .error text-align center color #F44336 -body[data-theme="monokai"] - .root - modalMonokai() - width 500px - height 270px - overflow hidden - position relative +apply-theme(theme) + body[data-theme={theme}] + .root + background-color transparent + width 500px + height 270px + overflow hidden + position relative - .header - background-color transparent - border-color $ui-dark-borderColor - color $ui-monokai-text-color + .header + background-color transparent + border-color get-theme-var(theme, 'borderColor') + color get-theme-var(theme, 'text-color') - .control-folder-label - color $ui-monokai-text-color + .control-folder-label + color get-theme-var(theme, 'text-color') - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white + .control-folder-input + border 1px solid $ui-input--create-folder-modal + color white - .description - color $ui-inactive-text-color + .description + color $ui-inactive-text-color - .control-confirmButton - colorMonokaiPrimaryButton() + .control-confirmButton + colorThemedPrimaryButton(theme) -body[data-theme="dracula"] - .root - modalDracula() - width 500px - height 270px - overflow hidden - position relative +for theme in 'dark' 'dracula' 'solarized-dark' + apply-theme(theme) - .header - background-color transparent - border-color $ui-dracula-borderColor - color $ui-dracula-text-color - - .control-folder-label - color $ui-dracula-text-color - - .control-folder-input - border 1px solid $ui-input--create-folder-modal - color white - - .description - color $ui-inactive-text-color - - .control-confirmButton - colorDraculaPrimaryButton() +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/NewNoteModal.js b/browser/main/modals/NewNoteModal.js index 476fa252..c06e91e3 100644 --- a/browser/main/modals/NewNoteModal.js +++ b/browser/main/modals/NewNoteModal.js @@ -9,21 +9,21 @@ import { createMarkdownNote, createSnippetNote } from 'browser/lib/newNote' import queryString from 'query-string' class NewNoteModal extends React.Component { - constructor (props) { + constructor(props) { super(props) this.lock = false this.state = {} } - componentDidMount () { + componentDidMount() { this.refs.markdownButton.focus() } - handleCloseButtonClick (e) { + handleCloseButtonClick(e) { this.props.close() } - handleCreateMarkdownFromUrlClick (e) { + handleCreateMarkdownFromUrlClick(e) { this.props.close() const { storage, folder, dispatch, location } = this.props @@ -35,49 +35,63 @@ class NewNoteModal extends React.Component { }) } - handleMarkdownNoteButtonClick (e) { + handleMarkdownNoteButtonClick(e) { const { storage, folder, dispatch, location, config } = this.props const params = location.search !== '' && queryString.parse(location.search) if (!this.lock) { this.lock = true - createMarkdownNote(storage, folder, dispatch, location, params, config).then(() => { + createMarkdownNote( + storage, + folder, + dispatch, + location, + params, + config + ).then(() => { setTimeout(this.props.close, 200) }) } } - handleMarkdownNoteButtonKeyDown (e) { + handleMarkdownNoteButtonKeyDown(e) { if (e.keyCode === 9) { e.preventDefault() this.refs.snippetButton.focus() } } - handleSnippetNoteButtonClick (e) { + handleSnippetNoteButtonClick(e) { const { storage, folder, dispatch, location, config } = this.props const params = location.search !== '' && queryString.parse(location.search) if (!this.lock) { this.lock = true - createSnippetNote(storage, folder, dispatch, location, params, config).then(() => { + createSnippetNote( + storage, + folder, + dispatch, + location, + params, + config + ).then(() => { setTimeout(this.props.close, 200) }) } } - handleSnippetNoteButtonKeyDown (e) { + handleSnippetNoteButtonKeyDown(e) { if (e.keyCode === 9) { e.preventDefault() this.refs.markdownButton.focus() } } - handleKeyDown (e) { + handleKeyDown(e) { if (e.keyCode === 27) { this.props.close() } } - render () { + render() { return (
this.handleSnippetNoteButtonKeyDown(e)} ref='snippetButton' > -
+ +
{i18n.__('Snippet Note')} @@ -127,10 +142,17 @@ class NewNoteModal extends React.Component { )} -
-
{i18n.__('Tab to switch format')}
-
this.handleCreateMarkdownFromUrlClick(e)}>Or, create a new markdown note from a URL
+
+ + {i18n.__('Tab to switch format')} +
+
this.handleCreateMarkdownFromUrlClick(e)} + > + Or, create a new markdown note from a URL +
) } diff --git a/browser/main/modals/NewNoteModal.styl b/browser/main/modals/NewNoteModal.styl index ff0052bd..a9be5a22 100644 --- a/browser/main/modals/NewNoteModal.styl +++ b/browser/main/modals/NewNoteModal.styl @@ -54,70 +54,26 @@ margin-bottom 25px cursor pointer -body[data-theme="dark"] - .root - modalDark() +apply-theme(theme) + body[data-theme={theme}] + .root + background-color transparent - .header - color $ui-dark-text-color + .header + color get-theme-var(theme, 'text-color') - .control-button - border-color $ui-dark-borderColor - color $ui-dark-text-color - background-color transparent - &:focus - colorDarkPrimaryButton() + .control-button + border-color get-theme-var(theme, 'borderColor') + color get-theme-var(theme, 'text-color') + background-color transparent + &:focus + colorThemedPrimaryButton(theme) - .description, .from-url - color $ui-inactive-text-color + .description + color get-theme-var(theme, 'text-color') -body[data-theme="solarized-dark"] - .root - background-color transparent +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) - .header - color $ui-solarized-dark-text-color - - .control-button - border-color $ui-solarized-dark-borderColor - color $ui-solarized-dark-text-color - background-color transparent - &:focus - colorDarkPrimaryButton() - - .description, .from-url - color $ui-solarized-dark-text-color - -body[data-theme="monokai"] - .root - background-color transparent - - .header - color $ui-monokai-text-color - - .control-button - border-color $ui-monokai-borderColor - color $ui-monokai-text-color - background-color transparent - &:focus - colorDarkPrimaryButton() - - .description, .from-url - color $ui-monokai-text-color - -body[data-theme="dracula"] - .root - background-color transparent - - .header - color $ui-dracula-text-color - - .control-button - border-color $ui-dracula-borderColor - color $ui-dracula-text-color - background-color transparent - &:focus - colorDraculaPrimaryButton() - - .description - color $ui-dracula-text-color \ No newline at end of file +for theme in $themes + apply-theme(theme) diff --git a/browser/main/modals/PreferencesModal/Blog.js b/browser/main/modals/PreferencesModal/Blog.js index 4d59bea1..26b4839d 100644 --- a/browser/main/modals/PreferencesModal/Blog.js +++ b/browser/main/modals/PreferencesModal/Blog.js @@ -11,7 +11,7 @@ const electron = require('electron') const { shell } = electron const ipc = electron.ipcRenderer class Blog extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -20,12 +20,12 @@ class Blog extends React.Component { } } - handleLinkClick (e) { + handleLinkClick(e) { shell.openExternal(e.currentTarget.href) e.preventDefault() } - clearMessage () { + clearMessage() { _.debounce(() => { this.setState({ BlogAlert: null @@ -33,30 +33,41 @@ class Blog extends React.Component { }, 2000)() } - componentDidMount () { + componentDidMount() { this.handleSettingDone = () => { - this.setState({BlogAlert: { - type: 'success', - message: i18n.__('Successfully applied!') - }}) + this.setState({ + BlogAlert: { + type: 'success', + message: i18n.__('Successfully applied!') + } + }) } - this.handleSettingError = (err) => { - this.setState({BlogAlert: { - type: 'error', - message: err.message != null ? err.message : i18n.__('An error occurred!') - }}) + this.handleSettingError = err => { + this.setState({ + BlogAlert: { + type: 'error', + message: + err.message != null ? err.message : i18n.__('An error occurred!') + } + }) } this.oldBlog = this.state.config.blog ipc.addListener('APP_SETTING_DONE', this.handleSettingDone) ipc.addListener('APP_SETTING_ERROR', this.handleSettingError) } - handleBlogChange (e) { + handleBlogChange(e) { const { config } = this.state config.blog = { - password: !_.isNil(this.refs.passwordInput) ? this.refs.passwordInput.value : config.blog.password, - username: !_.isNil(this.refs.usernameInput) ? this.refs.usernameInput.value : config.blog.username, - token: !_.isNil(this.refs.tokenInput) ? this.refs.tokenInput.value : config.blog.token, + password: !_.isNil(this.refs.passwordInput) + ? this.refs.passwordInput.value + : config.blog.password, + username: !_.isNil(this.refs.usernameInput) + ? this.refs.usernameInput.value + : config.blog.username, + token: !_.isNil(this.refs.tokenInput) + ? this.refs.tokenInput.value + : config.blog.token, authMethod: this.refs.authMethodDropdown.value, address: this.refs.addressInput.value, type: this.refs.typeDropdown.value @@ -75,7 +86,7 @@ class Blog extends React.Component { } } - handleSaveButtonClick (e) { + handleSaveButtonClick(e) { const newConfig = { blog: this.state.config.blog } @@ -90,36 +101,36 @@ class Blog extends React.Component { this.props.haveToSave() } - render () { - const {config, BlogAlert} = this.state - const blogAlertElement = BlogAlert != null - ?

- {BlogAlert.message} -

- : null + render() { + const { config, BlogAlert } = this.state + const blogAlertElement = + BlogAlert != null ? ( +

{BlogAlert.message}

+ ) : null return (
{i18n.__('Blog')}
-
- {i18n.__('Blog Type')} -
+
{i18n.__('Blog Type')}
{i18n.__('Blog Address')}
- this.handleBlogChange(e)} + this.handleBlogChange(e)} ref='addressInput' value={config.blog.address} type='text' @@ -127,8 +138,11 @@ class Blog extends React.Component {
- {blogAlertElement}
@@ -143,49 +157,59 @@ class Blog extends React.Component {
- { config.blog.authMethod === 'JWT' && + {config.blog.authMethod === 'JWT' && (
{i18n.__('Token')}
- this.handleBlogChange(e)} + this.handleBlogChange(e)} ref='tokenInput' value={config.blog.token} - type='text' /> + type='text' + />
- } - { config.blog.authMethod === 'USER' && + )} + {config.blog.authMethod === 'USER' && (
{i18n.__('UserName')}
- this.handleBlogChange(e)} + this.handleBlogChange(e)} ref='usernameInput' value={config.blog.username} - type='text' /> + type='text' + />
{i18n.__('Password')}
- this.handleBlogChange(e)} + this.handleBlogChange(e)} ref='passwordInput' value={config.blog.password} - type='password' /> + type='password' + />
- } + )}
) } diff --git a/browser/main/modals/PreferencesModal/ConfigTab.styl b/browser/main/modals/PreferencesModal/ConfigTab.styl index 709bc9e1..c27bd3ac 100644 --- a/browser/main/modals/PreferencesModal/ConfigTab.styl +++ b/browser/main/modals/PreferencesModal/ConfigTab.styl @@ -106,7 +106,6 @@ div[id^="firstRow"] font-size $tab--button-font-size .root padding 15px - color $ui-text-color margin-bottom 30px .group @@ -118,7 +117,6 @@ div[id^="firstRow"] .group-header2 font-size 20px - color $ui-text-color margin-bottom 15px margin-top 30px @@ -240,20 +238,18 @@ colorDarkControl() background-color $ui-dark-backgroundColor color $ui-dark-text-color -colorSolarizedDarkControl() +colorThemedControl(theme) border none - background-color $ui-solarized-dark-button-backgroundColor - color $ui-solarized-dark-text-color + background-color get-theme-var(theme, 'button-backgroundColor') + color get-theme-var(theme, 'text-color') -colorMonokaiControl() - border none - background-color $ui-monokai-button-backgroundColor - color $ui-monokai-text-color +body[data-theme="default"], +body[data-theme="white"] + .root + color $ui-text-color -colorDraculaControl() - border none - background-color $ui-dracula-button-backgroundColor - color $ui-dracula-text-color + .group-header2 + color $ui-text-color body[data-theme="dark"] .root @@ -283,123 +279,44 @@ body[data-theme="dark"] .group-section-control select, .group-section-control-input colorDarkControl() - - .box-minmax, .rs-range, .rs-label - colorDarkControl() .rs-label - background none - .rs-range - &::-webkit-slider-thumb - colorDarkControl() - background $ui-dark-text-color + color $ui-dark-text-color -body[data-theme="solarized-dark"] - .root - color $ui-solarized-dark-text-color - .group-header - .group-header--sub - color $ui-solarized-dark-text-color - border-color $ui-solarized-dark-borderColor +apply-theme(theme) + body[data-theme={theme}] + .root + color get-theme-var(theme, 'text-color') - .group-header2 - .group-header2--sub - color $ui-solarized-dark-text-color + .group-header + .group-header--sub + color get-theme-var(theme, 'text-color') + border-color get-theme-var(theme, 'borderColor') - .group-section-control-input - border-color $ui-solarized-dark-borderColor + .group-header2 + .group-header2--sub + color get-theme-var(theme, 'text-color') - .group-control - border-color $ui-solarized-dark-borderColor - .group-control-leftButton - colorDarkDefaultButton() - border-color $ui-solarized-dark-borderColor - .group-control-rightButton - colorSolarizedDarkPrimaryButton() - .group-hint - colorSolarizedDarkControl() - .group-section-control - select, .group-section-control-input - colorSolarizedDarkControl() - .box-minmax, .rs-range, .rs-label - colorSolarizedDarkControl() - .rs-label - background none - .rs-range - &::-webkit-slider-thumb - colorSolarizedDarkControl() - background $ui-solarized-dark-text-color + .group-section-control-input + border-color get-theme-var(theme, 'borderColor') -body[data-theme="monokai"] - .root - color $ui-monokai-text-color + .group-control + border-color get-theme-var(theme, 'borderColor') + .group-control-leftButton + colorDarkDefaultButton() + border-color get-theme-var(theme, 'borderColor') + .group-control-rightButton + colorThemedPrimaryButton(theme) + .group-hint + colorThemedControl(theme) + .group-section-control + select, .group-section-control-input + colorThemedControl(theme) + .rs-label + color get-theme-var(theme, 'text-color') - .group-header - .group-header--sub - color $ui-monokai-text-color - border-color $ui-monokai-borderColor +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) - .group-header2 - .group-header2--sub - color $ui-monokai-text-color - - .group-section-control-input - border-color $ui-monokai-borderColor - - .group-control - border-color $ui-monokai-borderColor - .group-control-leftButton - colorDarkDefaultButton() - border-color $ui-monokai-borderColor - .group-control-rightButton - colorMonokaiPrimaryButton() - .group-hint - colorMonokaiControl() - .group-section-control - select, .group-section-control-input - colorMonokaiControl() - .box-minmax, .rs-range, .rs-label - colorMonokaiControl() - .rs-label - background none - .rs-range - &::-webkit-slider-thumb - colorMonokaiControl() - background $ui-monokai-text-color - -body[data-theme="dracula"] - .root - color $ui-dracula-text-color - - .group-header - .group-header--sub - color $ui-dracula-text-color - border-color $ui-dracula-borderColor - - .group-header2 - .group-header2--sub - color $ui-dracula-text-color - - .group-section-control-input - border-color $ui-dracula-borderColor - - .group-control - border-color $ui-dracula-borderColor - .group-control-leftButton - colorDarkDefaultButton() - border-color $ui-dracula-borderColor - .group-control-rightButton - colorDraculaPrimaryButton() - .group-hint - colorDraculaControl() - .group-section-control - select, .group-section-control-input - colorDraculaControl() - .box-minmax, .rs-range, .rs-label - colorDraculaControl() - .rs-label - background none - .rs-range - &::-webkit-slider-thumb - colorDraculaControl() - background $ui-dracula-text-color \ No newline at end of file +for theme in $themes + apply-theme(theme) diff --git a/browser/main/modals/PreferencesModal/Crowdfunding.js b/browser/main/modals/PreferencesModal/Crowdfunding.js index 56bb6e34..a5d37718 100644 --- a/browser/main/modals/PreferencesModal/Crowdfunding.js +++ b/browser/main/modals/PreferencesModal/Crowdfunding.js @@ -7,50 +7,93 @@ const electron = require('electron') const { shell } = electron class Crowdfunding extends React.Component { - constructor (props) { + constructor(props) { super(props) - this.state = { - } + this.state = {} } - handleLinkClick (e) { + handleLinkClick(e) { shell.openExternal(e.currentTarget.href) e.preventDefault() } - render () { + render() { return (
{i18n.__('Crowdfunding')}

{i18n.__('Thank you for using Boostnote!')}


-

{i18n.__('We launched IssueHunt which is an issue-based crowdfunding / sourcing platform for open source projects.')}

-

{i18n.__('Anyone can put a bounty on not only a bug but also on OSS feature requests listed on IssueHunt. Collected funds will be distributed to project owners and contributors.')}

-
{i18n.__('Sustainable Open Source Ecosystem')}
-

{i18n.__('We discussed about open-source ecosystem and IssueHunt concept with the Boostnote team repeatedly. We actually also discussed with Matz who father of Ruby.')}

-

{i18n.__('The original reason why we made IssueHunt was to reward our contributors of Boostnote project. We’ve got tons of Github stars and hundred of contributors in two years.')}

-

{i18n.__('We thought that it will be nice if we can pay reward for our contributors.')}

-
{i18n.__('We believe Meritocracy')}
-

{i18n.__('We think developers who have skills and do great things must be rewarded properly.')}

-

{i18n.__('OSS projects are used in everywhere on the internet, but no matter how they great, most of owners of those projects need to have another job to sustain their living.')}

+

+ {i18n.__( + 'We launched IssueHunt which is an issue-based crowdfunding / sourcing platform for open source projects.' + )} +

+

+ {i18n.__( + 'Anyone can put a bounty on not only a bug but also on OSS feature requests listed on IssueHunt. Collected funds will be distributed to project owners and contributors.' + )} +

+
+ {i18n.__('Sustainable Open Source Ecosystem')} +
+

+ {i18n.__( + 'We discussed about open-source ecosystem and IssueHunt concept with the Boostnote team repeatedly. We actually also discussed with Matz who father of Ruby.' + )} +

+

+ {i18n.__( + 'The original reason why we made IssueHunt was to reward our contributors of Boostnote project. We’ve got tons of Github stars and hundred of contributors in two years.' + )} +

+

+ {i18n.__( + 'We thought that it will be nice if we can pay reward for our contributors.' + )} +

+
+ {i18n.__('We believe Meritocracy')} +
+

+ {i18n.__( + 'We think developers who have skills and do great things must be rewarded properly.' + )} +

+

+ {i18n.__( + 'OSS projects are used in everywhere on the internet, but no matter how they great, most of owners of those projects need to have another job to sustain their living.' + )} +

{i18n.__('It sometimes looks like exploitation.')}

-

{i18n.__('We’ve realized IssueHunt could enhance sustainability of open-source ecosystem.')}

+

+ {i18n.__( + 'We’ve realized IssueHunt could enhance sustainability of open-source ecosystem.' + )} +


-

{i18n.__('As same as issues of Boostnote are already funded on IssueHunt, your open-source projects can be also started funding from now.')}

+

+ {i18n.__( + 'As same as issues of Boostnote are already funded on IssueHunt, your open-source projects can be also started funding from now.' + )} +


{i18n.__('Thank you,')}

{i18n.__('The Boostnote Team')}


) } } -Crowdfunding.propTypes = { -} +Crowdfunding.propTypes = {} export default CSSModules(Crowdfunding, styles) diff --git a/browser/main/modals/PreferencesModal/Crowdfunding.styl b/browser/main/modals/PreferencesModal/Crowdfunding.styl index d1d6fc9f..4725aa23 100644 --- a/browser/main/modals/PreferencesModal/Crowdfunding.styl +++ b/browser/main/modals/PreferencesModal/Crowdfunding.styl @@ -24,20 +24,15 @@ body[data-theme="dark"] p color $ui-dark-text-color -body[data-theme="solarized-dark"] - .root - color $ui-solarized-dark-text-color - p - color $ui-solarized-dark-text-color +apply-theme(theme) + body[data-theme={theme}] + .root + color get-theme-var(theme, 'text-color') + p + color get-theme-var(theme, 'text-color') -body[data-theme="monokai"] - .root - color $ui-monokai-text-color - p - color $ui-monokai-text-color +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="dracula"] - .root - color $ui-dracula-text-color - p - color $ui-dracula-text-color \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/PreferencesModal/FolderItem.js b/browser/main/modals/PreferencesModal/FolderItem.js index 648db4e6..6418bb6a 100644 --- a/browser/main/modals/PreferencesModal/FolderItem.js +++ b/browser/main/modals/PreferencesModal/FolderItem.js @@ -10,7 +10,7 @@ import { SortableElement, SortableHandle } from 'react-sortable-hoc' import i18n from 'browser/lib/i18n' class FolderItem extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -24,7 +24,7 @@ class FolderItem extends React.Component { } } - handleEditChange (e) { + handleEditChange(e) { const { folder } = this.state folder.name = this.refs.nameInput.value @@ -33,18 +33,18 @@ class FolderItem extends React.Component { }) } - handleConfirmButtonClick (e) { + handleConfirmButtonClick(e) { this.confirm() } - confirm () { + confirm() { const { storage, folder } = this.props dataApi .updateFolder(storage.key, folder.key, { color: this.state.folder.color, name: this.state.folder.name }) - .then((data) => { + .then(data => { store.dispatch({ type: 'UPDATE_FOLDER', storage: data.storage @@ -55,9 +55,12 @@ class FolderItem extends React.Component { }) } - handleColorButtonClick (e) { - const folder = Object.assign({}, this.state.folder, { showColumnPicker: true, colorPickerPos: { left: 0, top: 0 } }) - this.setState({ folder }, function () { + handleColorButtonClick(e) { + const folder = Object.assign({}, this.state.folder, { + showColumnPicker: true, + colorPickerPos: { left: 0, top: 0 } + }) + this.setState({ folder }, function() { // After the color picker has been painted, re-calculate its position // by comparing its dimensions to the host dimensions. const { hostBoundingBox } = this.props @@ -67,30 +70,32 @@ class FolderItem extends React.Component { const folder = Object.assign({}, this.state.folder, { colorPickerPos: { left: 25, - top: offsetTop < 0 ? offsetTop - 5 : 0 // subtract 5px for aestetics + top: offsetTop < 0 ? offsetTop - 5 : 0 // subtract 5px for aestetics } }) this.setState({ folder }) }) } - handleColorChange (color) { + handleColorChange(color) { const folder = Object.assign({}, this.state.folder, { color: color.hex }) this.setState({ folder }) } - handleColorPickerClose (event) { - const folder = Object.assign({}, this.state.folder, { showColumnPicker: false }) + handleColorPickerClose(event) { + const folder = Object.assign({}, this.state.folder, { + showColumnPicker: false + }) this.setState({ folder }) } - handleCancelButtonClick (e) { + handleCancelButtonClick(e) { this.setState({ status: 'IDLE' }) } - handleFolderItemBlur (e) { + handleFolderItemBlur(e) { let el = e.relatedTarget while (el != null) { if (el === this.refs.root) { @@ -101,7 +106,7 @@ class FolderItem extends React.Component { this.confirm() } - renderEdit (e) { + renderEdit(e) { const popover = { position: 'absolute', zIndex: 2 } const cover = { position: 'fixed', @@ -110,51 +115,64 @@ class FolderItem extends React.Component { bottom: 0, left: 0 } - const pickerStyle = Object.assign({}, { - position: 'absolute' - }, this.state.folder.colorPickerPos) + const pickerStyle = Object.assign( + {}, + { + position: 'absolute' + }, + this.state.folder.colorPickerPos + ) return ( -
this.handleFolderItemBlur(e)} +
this.handleFolderItemBlur(e)} tabIndex='-1' ref='root' >
- - this.handleEditChange(e)} + onChange={e => this.handleEditChange(e)} />
- - @@ -163,79 +181,85 @@ class FolderItem extends React.Component { ) } - handleDeleteConfirmButtonClick (e) { + handleDeleteConfirmButtonClick(e) { const { storage, folder } = this.props - dataApi - .deleteFolder(storage.key, folder.key) - .then((data) => { - store.dispatch({ - type: 'DELETE_FOLDER', - storage: data.storage, - folderKey: data.folderKey - }) + dataApi.deleteFolder(storage.key, folder.key).then(data => { + store.dispatch({ + type: 'DELETE_FOLDER', + storage: data.storage, + folderKey: data.folderKey }) - } - - renderDelete () { - return ( -
-
- {i18n.__('Are you sure to ')} {i18n.__(' delete')} {i18n.__('this folder?')} -
-
- - -
-
- ) - } - - handleEditButtonClick (e) { - const { folder: propsFolder } = this.props - const { folder: stateFolder } = this.state - const folder = Object.assign({}, stateFolder, propsFolder) - this.setState({ - status: 'EDIT', - folder - }, () => { - this.refs.nameInput.select() }) } - handleDeleteButtonClick (e) { + renderDelete() { + return ( +
+
+ {i18n.__('Are you sure to ')}{' '} + {i18n.__(' delete')}{' '} + {i18n.__('this folder?')} +
+
+ + +
+
+ ) + } + + handleEditButtonClick(e) { + const { folder: propsFolder } = this.props + const { folder: stateFolder } = this.state + const folder = Object.assign({}, stateFolder, propsFolder) + this.setState( + { + status: 'EDIT', + folder + }, + () => { + this.refs.nameInput.select() + } + ) + } + + handleDeleteButtonClick(e) { this.setState({ status: 'DELETE' }) } - renderIdle () { + renderIdle() { const { folder } = this.props return ( -
this.handleEditButtonClick(e)} +
this.handleEditButtonClick(e)} > -
+
{folder.name} ({folder.key})
- - @@ -244,7 +268,7 @@ class FolderItem extends React.Component { ) } - render () { + render() { switch (this.state.status) { case 'DELETE': return this.renderDelete() @@ -277,7 +301,7 @@ FolderItem.propTypes = { } class Handle extends React.Component { - render () { + render() { return (
@@ -287,7 +311,7 @@ class Handle extends React.Component { } class SortableFolderItemComponent extends React.Component { - render () { + render() { const StyledHandle = CSSModules(Handle, styles) const DragHandle = SortableHandle(StyledHandle) diff --git a/browser/main/modals/PreferencesModal/FolderItem.styl b/browser/main/modals/PreferencesModal/FolderItem.styl index 618e9bc4..32a83d63 100644 --- a/browser/main/modals/PreferencesModal/FolderItem.styl +++ b/browser/main/modals/PreferencesModal/FolderItem.styl @@ -107,73 +107,32 @@ body[data-theme="dark"] .folderItem-right-dangerButton colorDarkDangerButton() +apply-theme(theme) + body[data-theme={theme}] + .folderItem + &:hover + background-color get-theme-var(theme, 'button-backgroundColor') + .folderItem-left-danger + color $danger-color -body[data-theme="solarized-dark"] - .folderItem - &:hover - background-color $ui-solarized-dark-button-backgroundColor + .folderItem-left-key + color $ui-dark-inactive-text-color - .folderItem-left-danger - color $danger-color + .folderItem-left-colorButton + colorThemedPrimaryButton(theme) - .folderItem-left-key - color $ui-dark-inactive-text-color + .folderItem-right-button + colorThemedPrimaryButton(theme) - .folderItem-left-colorButton - colorSolarizedDarkPrimaryButton() + .folderItem-right-confirmButton + colorThemedPrimaryButton(theme) - .folderItem-right-button - colorSolarizedDarkPrimaryButton() + .folderItem-right-dangerButton + colorThemedPrimaryButton(theme) - .folderItem-right-confirmButton - colorSolarizedDarkPrimaryButton() +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) - .folderItem-right-dangerButton - colorSolarizedDarkPrimaryButton() - -body[data-theme="monokai"] - .folderItem - &:hover - background-color $ui-monokai-button-backgroundColor - - .folderItem-left-danger - color $danger-color - - .folderItem-left-key - color $ui-dark-inactive-text-color - - .folderItem-left-colorButton - colorMonokaiPrimaryButton() - - .folderItem-right-button - colorMonokaiPrimaryButton() - - .folderItem-right-confirmButton - colorMonokaiPrimaryButton() - - .folderItem-right-dangerButton - colorMonokaiPrimaryButton() - -body[data-theme="dracula"] - .folderItem - &:hover - background-color $ui-dracula-button-backgroundColor - - .folderItem-left-danger - color $danger-color - - .folderItem-left-key - color $ui-dark-inactive-text-color - - .folderItem-left-colorButton - colorDraculaPrimaryButton() - - .folderItem-right-button - colorDraculaPrimaryButton() - - .folderItem-right-confirmButton - colorDraculaPrimaryButton() - - .folderItem-right-dangerButton - colorDraculaPrimaryButton() \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/PreferencesModal/FolderList.js b/browser/main/modals/PreferencesModal/FolderList.js index 674026c5..f751d250 100644 --- a/browser/main/modals/PreferencesModal/FolderList.js +++ b/browser/main/modals/PreferencesModal/FolderList.js @@ -9,24 +9,28 @@ import { SortableContainer } from 'react-sortable-hoc' import i18n from 'browser/lib/i18n' class FolderList extends React.Component { - render () { + render() { const { storage, hostBoundingBox } = this.props const folderList = storage.folders.map((folder, index) => { - return + return ( + + ) }) return (
- {folderList.length > 0 - ? folderList - :
{i18n.__('No Folders')}
- } + {folderList.length > 0 ? ( + folderList + ) : ( +
{i18n.__('No Folders')}
+ )}
) } @@ -52,23 +56,21 @@ FolderList.propTypes = { } class SortableFolderListComponent extends React.Component { - constructor (props) { + constructor(props) { super(props) - this.onSortEnd = ({oldIndex, newIndex}) => { + this.onSortEnd = ({ oldIndex, newIndex }) => { const { storage } = this.props - dataApi - .reorderFolder(storage.key, oldIndex, newIndex) - .then((data) => { - store.dispatch({ - type: 'REORDER_FOLDER', - storage: data.storage - }) - this.setState() + dataApi.reorderFolder(storage.key, oldIndex, newIndex).then(data => { + store.dispatch({ + type: 'REORDER_FOLDER', + storage: data.storage }) + this.setState() + }) } } - render () { + render() { const StyledFolderList = CSSModules(FolderList, this.props.styles) const SortableFolderList = SortableContainer(StyledFolderList) diff --git a/browser/main/modals/PreferencesModal/HotkeyTab.js b/browser/main/modals/PreferencesModal/HotkeyTab.js index 9c4f5655..1bba78e3 100644 --- a/browser/main/modals/PreferencesModal/HotkeyTab.js +++ b/browser/main/modals/PreferencesModal/HotkeyTab.js @@ -11,7 +11,7 @@ const electron = require('electron') const ipc = electron.ipcRenderer class HotkeyTab extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -20,27 +20,35 @@ class HotkeyTab extends React.Component { } } - componentDidMount () { + componentDidMount() { this.handleSettingDone = () => { - this.setState({keymapAlert: { - type: 'success', - message: i18n.__('Successfully applied!') - }}) - } - this.handleSettingError = (err) => { - if ( - this.state.config.hotkey.toggleMain === '' || - this.state.config.hotkey.toggleMode === '' - ) { - this.setState({keymapAlert: { + this.setState({ + keymapAlert: { type: 'success', message: i18n.__('Successfully applied!') - }}) + } + }) + } + this.handleSettingError = err => { + if ( + this.state.config.hotkey.toggleMain === '' || + this.state.config.hotkey.toggleMode === '' || + this.state.config.hotkey.toggleDirection === '' + ) { + this.setState({ + keymapAlert: { + type: 'success', + message: i18n.__('Successfully applied!') + } + }) } else { - this.setState({keymapAlert: { - type: 'error', - message: err.message != null ? err.message : i18n.__('An error occurred!') - }}) + this.setState({ + keymapAlert: { + type: 'error', + message: + err.message != null ? err.message : i18n.__('An error occurred!') + } + }) } } this.oldHotkey = this.state.config.hotkey @@ -48,12 +56,12 @@ class HotkeyTab extends React.Component { ipc.addListener('APP_SETTING_ERROR', this.handleSettingError) } - componentWillUnmount () { + componentWillUnmount() { ipc.removeListener('APP_SETTING_DONE', this.handleSettingDone) ipc.removeListener('APP_SETTING_ERROR', this.handleSettingError) } - handleSaveButtonClick (e) { + handleSaveButtonClick(e) { const newConfig = { hotkey: this.state.config.hotkey } @@ -68,17 +76,18 @@ class HotkeyTab extends React.Component { this.props.haveToSave() } - handleHintToggleButtonClick (e) { + handleHintToggleButtonClick(e) { this.setState({ isHotkeyHintOpen: !this.state.isHotkeyHintOpen }) } - handleHotkeyChange (e) { + handleHotkeyChange(e) { const { config } = this.state config.hotkey = Object.assign({}, config.hotkey, { toggleMain: this.refs.toggleMain.value, toggleMode: this.refs.toggleMode.value, + toggleDirection: this.refs.toggleDirection.value, deleteNote: this.refs.deleteNote.value, pasteSmartly: this.refs.pasteSmartly.value, prettifyMarkdown: this.refs.prettifyMarkdown.value, @@ -100,7 +109,7 @@ class HotkeyTab extends React.Component { } } - clearMessage () { + clearMessage() { _.debounce(() => { this.setState({ keymapAlert: null @@ -108,13 +117,12 @@ class HotkeyTab extends React.Component { }, 2000)() } - render () { + render() { const keymapAlert = this.state.keymapAlert - const keymapAlertElement = keymapAlert != null - ?

- {keymapAlert.message} -

- : null + const keymapAlertElement = + keymapAlert != null ? ( +

{keymapAlert.message}

+ ) : null const { config } = this.state return ( @@ -122,10 +130,13 @@ class HotkeyTab extends React.Component {
{i18n.__('Hotkeys')}
-
{i18n.__('Show/Hide Boostnote')}
+
+ {i18n.__('Show/Hide Boostnote')} +
- this.handleHotkeyChange(e)} + this.handleHotkeyChange(e)} ref='toggleMain' value={config.hotkey.toggleMain} type='text' @@ -133,10 +144,13 @@ class HotkeyTab extends React.Component {
-
{i18n.__('Show/Hide Menu Bar')}
+
+ {i18n.__('Show/Hide Menu Bar')} +
- this.handleHotkeyChange(e)} + this.handleHotkeyChange(e)} ref='toggleMenuBar' value={config.hotkey.toggleMenuBar} type='text' @@ -144,21 +158,39 @@ class HotkeyTab extends React.Component {
-
{i18n.__('Toggle Editor Mode')}
+
+ {i18n.__('Toggle Editor Mode')} +
- this.handleHotkeyChange(e)} + this.handleHotkeyChange(e)} ref='toggleMode' value={config.hotkey.toggleMode} type='text' />
+
+
+ {i18n.__('Toggle Direction')} +
+
+ this.handleHotkeyChange(e)} + ref='toggleDirection' + value={config.hotkey.toggleDirection} + type='text' + /> +
+
{i18n.__('Delete Note')}
- this.handleHotkeyChange(e)} + this.handleHotkeyChange(e)} ref='deleteNote' value={config.hotkey.deleteNote} type='text' @@ -168,8 +200,9 @@ class HotkeyTab extends React.Component {
{i18n.__('Paste HTML')}
- this.handleHotkeyChange(e)} + this.handleHotkeyChange(e)} ref='pasteSmartly' value={config.hotkey.pasteSmartly} type='text' @@ -177,19 +210,26 @@ class HotkeyTab extends React.Component {
-
{i18n.__('Prettify Markdown')}
+
+ {i18n.__('Prettify Markdown')} +
- this.handleHotkeyChange(e)} + this.handleHotkeyChange(e)} ref='prettifyMarkdown' value={config.hotkey.prettifyMarkdown} - type='text' /> + type='text' + />
-
{i18n.__('Insert Current Date')}
+
+ {i18n.__('Insert Current Date')} +
-
-
{i18n.__('Insert Current Date and Time')}
+
+ {i18n.__('Insert Current Date and Time')} +
-
- - {keymapAlertElement}
- {this.state.isHotkeyHintOpen && + {this.state.isHotkeyHintOpen && (

{i18n.__('Available Keys')}

    -
  • 0 to 9
  • -
  • A to Z
  • -
  • F1 to F24
  • -
  • Punctuations like ~, !, @, #, $, etc.
  • -
  • Plus
  • -
  • Space
  • -
  • Backspace
  • -
  • Delete
  • -
  • Insert
  • -
  • Return (or Enter as alias)
  • -
  • Up, Down, Left and Right
  • -
  • Home and End
  • -
  • PageUp and PageDown
  • -
  • Escape (or Esc for short)
  • -
  • VolumeUp, VolumeDown and VolumeMute
  • -
  • MediaNextTrack, MediaPreviousTrack, MediaStop and MediaPlayPause
  • -
  • Control (or Ctrl for short)
  • -
  • Shift
  • +
  • + 0 to 9 +
  • +
  • + A to Z +
  • +
  • + F1 to F24 +
  • +
  • + Punctuations like ~, !,{' '} + @, #, $, etc. +
  • +
  • + Plus +
  • +
  • + Space +
  • +
  • + Backspace +
  • +
  • + Delete +
  • +
  • + Insert +
  • +
  • + Return (or Enter as alias) +
  • +
  • + Up, Down, Left and{' '} + Right +
  • +
  • + Home and End +
  • +
  • + PageUp and PageDown +
  • +
  • + Escape (or Esc for short) +
  • +
  • + VolumeUp, VolumeDown and{' '} + VolumeMute +
  • +
  • + MediaNextTrack, MediaPreviousTrack,{' '} + MediaStop and MediaPlayPause +
  • +
  • + Control (or Ctrl for short) +
  • +
  • + Shift +
- } + )}
) diff --git a/browser/main/modals/PreferencesModal/InfoTab.js b/browser/main/modals/PreferencesModal/InfoTab.js index 66250412..fa49025f 100644 --- a/browser/main/modals/PreferencesModal/InfoTab.js +++ b/browser/main/modals/PreferencesModal/InfoTab.js @@ -12,7 +12,7 @@ const { shell, remote } = electron const appVersion = remote.app.getVersion() class InfoTab extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -20,18 +20,18 @@ class InfoTab extends React.Component { } } - handleLinkClick (e) { + handleLinkClick(e) { shell.openExternal(e.currentTarget.href) e.preventDefault() } - handleConfigChange (e) { + handleConfigChange(e) { const newConfig = { amaEnabled: this.refs.amaEnabled.checked } this.setState({ config: newConfig }) } - handleSaveButtonClick (e) { + handleSaveButtonClick(e) { const newConfig = { amaEnabled: this.state.config.amaEnabled } @@ -43,7 +43,7 @@ class InfoTab extends React.Component { }) } else { this.setState({ - amaMessage: i18n.__('Thank\'s for trusting us') + amaMessage: i18n.__("Thank's for trusting us") }) } @@ -61,7 +61,7 @@ class InfoTab extends React.Component { }) } - toggleAutoUpdate () { + toggleAutoUpdate() { const newConfig = { autoUpdateEnabled: !this.state.config.autoUpdateEnabled } @@ -70,46 +70,64 @@ class InfoTab extends React.Component { ConfigManager.set(newConfig) } - infoMessage () { + infoMessage() { const { amaMessage } = this.state return amaMessage ?

{amaMessage}

: null } - render () { + render() { return (
{i18n.__('Community')}
@@ -120,11 +138,20 @@ class InfoTab extends React.Component {
- +
-
{i18n.__('Boostnote')} {appVersion}
+
+ {i18n.__('Boostnote')} {appVersion} +
- {i18n.__('An open source note-taking app made for programmers just like you.')} + {i18n.__( + 'An open source note-taking app made for programmers just like you.' + )}
@@ -132,39 +159,71 @@ class InfoTab extends React.Component { -
+
+ +

{i18n.__('Analytics')}
-
{i18n.__('Boostnote collects anonymous data for the sole purpose of improving the application, and strictly does not collect any personal information such the contents of your notes.')}
-
{i18n.__('You can see how it works on ')} this.handleLinkClick(e)}>GitHub.
+
+ {i18n.__( + 'Boostnote collects anonymous data for the sole purpose of improving the application, and strictly does not collect any personal information such the contents of your notes.' + )} +
+
+ {i18n.__('You can see how it works on ')} + this.handleLinkClick(e)} + > + GitHub + + . +

{i18n.__('You can choose to enable or disable this option.')}
- this.handleConfigChange(e)} + this.handleConfigChange(e)} checked={this.state.config.amaEnabled} ref='amaEnabled' type='checkbox' /> - {i18n.__('Enable analytics to help improve Boostnote')}
- + {i18n.__('Enable analytics to help improve Boostnote')} +
+
{this.infoMessage()}
@@ -172,7 +231,6 @@ class InfoTab extends React.Component { } } -InfoTab.propTypes = { -} +InfoTab.propTypes = {} export default CSSModules(InfoTab, styles) diff --git a/browser/main/modals/PreferencesModal/InfoTab.styl b/browser/main/modals/PreferencesModal/InfoTab.styl index c541c91c..4701d809 100644 --- a/browser/main/modals/PreferencesModal/InfoTab.styl +++ b/browser/main/modals/PreferencesModal/InfoTab.styl @@ -59,29 +59,19 @@ body[data-theme="dark"] .appId color $ui-dark-text-color -body[data-theme="solarized-dark"] - .root - color $ui-solarized-dark-text-color - .appId - color $ui-solarized-dark-text-color -.list - a - color $ui-solarized-dark-active-color -body[data-theme="monokai"] - .root - color $ui-monokai-text-color - .appId - color $ui-monokai-text-color -.list - a - color $ui-monokai-active-color +apply-theme(theme) + body[data-theme={theme}] + .root + color get-theme-var(theme, 'text-color') + .appId + color get-theme-var(theme, 'text-color') + .list + a + color get-theme-var(theme, 'active-color') -body[data-theme="dracula"] - .root - color $ui-dracula-text-color - .appId - color $ui-dracula-text-color -.list - a - color $ui-dracula-active-color \ No newline at end of file +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) + +for theme in $themes + apply-theme(theme) diff --git a/browser/main/modals/PreferencesModal/PreferencesModal.styl b/browser/main/modals/PreferencesModal/PreferencesModal.styl index 7004259b..2e3b4040 100644 --- a/browser/main/modals/PreferencesModal/PreferencesModal.styl +++ b/browser/main/modals/PreferencesModal/PreferencesModal.styl @@ -64,102 +64,31 @@ top-bar--height = 50px margin-top 10px overflow-y auto -body[data-theme="dark"] - .root - modalDark() +apply-theme(theme) + body[data-theme={theme}] + .root + background-color transparent + .top-bar + background-color transparent + border-color get-theme-var(theme, 'borderColor') + p + color get-theme-var(theme, 'text-color') + .nav + background-color transparent + border-color get-theme-var(theme, 'borderColor') + .nav-button + background-color transparent + color get-theme-var(theme, 'text-color') + &:hover + color get-theme-var(theme, 'text-color') - .top-bar - background-color transparent - border-color #4A4D52 - p - color $tab--dark-text-color + .nav-button--active + @extend .nav-button + color get-theme-var(theme, 'button--active-color') + background-color get-theme-var(theme, 'button--active-backgroundColor') - .nav - background-color transparent - border-color $ui-dark-borderColor +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) - .nav-button - background-color transparent - color $tab--dark-text-color - &:hover - color $ui-dark-text-color - - .nav-button--active - @extend .nav-button - color white - background-color $dark-primary-button-background--active - &:hover - color white - - -body[data-theme="solarized-dark"] - .root - background-color transparent - .top-bar - background-color transparent - border-color $ui-solarized-dark-borderColor - p - color $ui-solarized-dark-text-color - .nav - background-color transparent - border-color $ui-solarized-dark-borderColor - .nav-button - background-color transparent - color $ui-solarized-dark-text-color - &:hover - color $ui-solarized-dark-text-color - - .nav-button--active - @extend .nav-button - color $ui-solarized-dark-button--active-color - background-color $ui-solarized-dark-button--active-backgroundColor - &:hover - color white - -body[data-theme="monokai"] - .root - background-color transparent - .top-bar - background-color transparent - border-color $ui-monokai-borderColor - p - color $ui-monokai-text-color - .nav - background-color transparent - border-color $ui-monokai-borderColor - .nav-button - background-color transparent - color $ui-monokai-text-color - &:hover - color $ui-monokai-text-color - - .nav-button--active - @extend .nav-button - color $ui-monokai-button--active-color - background-color $ui-monokai-button--active-backgroundColor - &:hover - color white - -body[data-theme="dracula"] - .root - background-color transparent - .top-bar - background-color transparent - border-color $ui-dracula-borderColor - p - color $ui-dracula-text-color - .nav - background-color transparent - border-color $ui-dracula-borderColor - .nav-button - background-color transparent - color $ui-dracula-text-color - &:hover - color $ui-dracula-text-color - - .nav-button--active - @extend .nav-button - color $ui-dracula-button--active-color - background-color $ui-dracula-button--active-backgroundColor - &:hover - color #f8f8f2 \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/PreferencesModal/SnippetEditor.js b/browser/main/modals/PreferencesModal/SnippetEditor.js index e95afdcf..f748924c 100644 --- a/browser/main/modals/PreferencesModal/SnippetEditor.js +++ b/browser/main/modals/PreferencesModal/SnippetEditor.js @@ -6,13 +6,19 @@ import CSSModules from 'browser/lib/CSSModules' import dataApi from 'browser/main/lib/dataApi' import snippetManager from '../../../lib/SnippetManager' -const defaultEditorFontFamily = ['Monaco', 'Menlo', 'Ubuntu Mono', 'Consolas', 'source-code-pro', 'monospace'] +const defaultEditorFontFamily = [ + 'Monaco', + 'Menlo', + 'Ubuntu Mono', + 'Consolas', + 'source-code-pro', + 'monospace' +] const buildCMRulers = (rulers, enableRulers) => enableRulers ? rulers.map(ruler => ({ column: ruler })) : [] class SnippetEditor extends React.Component { - - componentDidMount () { + componentDidMount() { this.props.onRef(this) const { rulers, enableRulers } = this.props this.cm = CodeMirror(this.refs.root, { @@ -49,38 +55,50 @@ class SnippetEditor extends React.Component { }) } - componentWillUnmount () { + componentWillUnmount() { this.props.onRef(undefined) } - onSnippetChanged (newSnippet) { + onSnippetChanged(newSnippet) { this.snippet = newSnippet this.cm.setValue(this.snippet.content) } - onSnippetNameOrPrefixChanged (newSnippet) { + onSnippetNameOrPrefixChanged(newSnippet) { this.snippet.name = newSnippet.name - this.snippet.prefix = newSnippet.prefix.toString().replace(/\s/g, '').split(',') + this.snippet.prefix = newSnippet.prefix + .toString() + .replace(/\s/g, '') + .split(',') this.saveSnippet() } - saveSnippet () { - dataApi.updateSnippet(this.snippet) + saveSnippet() { + dataApi + .updateSnippet(this.snippet) .then(snippets => snippetManager.assignSnippets(snippets)) - .catch((err) => { throw err }) + .catch(err => { + throw err + }) } - render () { + render() { const { fontSize } = this.props let fontFamily = this.props.fontFamily - fontFamily = _.isString(fontFamily) && fontFamily.length > 0 - ? [fontFamily].concat(defaultEditorFontFamily) - : defaultEditorFontFamily + fontFamily = + _.isString(fontFamily) && fontFamily.length > 0 + ? [fontFamily].concat(defaultEditorFontFamily) + : defaultEditorFontFamily return ( -
+
) } } diff --git a/browser/main/modals/PreferencesModal/SnippetList.js b/browser/main/modals/PreferencesModal/SnippetList.js index 3790eb3f..145cfb75 100644 --- a/browser/main/modals/PreferencesModal/SnippetList.js +++ b/browser/main/modals/PreferencesModal/SnippetList.js @@ -7,53 +7,65 @@ import eventEmitter from 'browser/main/lib/eventEmitter' import context from 'browser/lib/context' class SnippetList extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { snippets: [] } } - componentDidMount () { + componentDidMount() { this.reloadSnippetList() eventEmitter.on('snippetList:reload', this.reloadSnippetList.bind(this)) } - reloadSnippetList () { + reloadSnippetList() { dataApi.fetchSnippet().then(snippets => { - this.setState({snippets}) + this.setState({ snippets }) this.props.onSnippetSelect(this.props.currentSnippet) }) } - handleSnippetContextMenu (snippet) { - context.popup([{ - label: i18n.__('Delete snippet'), - click: () => this.deleteSnippet(snippet) - }]) + handleSnippetContextMenu(snippet) { + context.popup([ + { + label: i18n.__('Delete snippet'), + click: () => this.deleteSnippet(snippet) + } + ]) } - deleteSnippet (snippet) { - dataApi.deleteSnippet(snippet).then(() => { - this.reloadSnippetList() - this.props.onSnippetDeleted(snippet) - }).catch(err => { throw err }) + deleteSnippet(snippet) { + dataApi + .deleteSnippet(snippet) + .then(() => { + this.reloadSnippetList() + this.props.onSnippetDeleted(snippet) + }) + .catch(err => { + throw err + }) } - handleSnippetClick (snippet) { + handleSnippetClick(snippet) { this.props.onSnippetSelect(snippet) } - createSnippet () { - dataApi.createSnippet().then(() => { - this.reloadSnippetList() - // scroll to end of list when added new snippet - const snippetList = document.getElementById('snippets') - snippetList.scrollTop = snippetList.scrollHeight - }).catch(err => { throw err }) + createSnippet() { + dataApi + .createSnippet() + .then(() => { + this.reloadSnippetList() + // scroll to end of list when added new snippet + const snippetList = document.getElementById('snippets') + snippetList.scrollTop = snippetList.scrollHeight + }) + .catch(err => { + throw err + }) } - defineSnippetStyleName (snippet) { + defineSnippetStyleName(snippet) { const { currentSnippet } = this.props if (currentSnippet == null) { @@ -67,29 +79,31 @@ class SnippetList extends React.Component { } } - render () { + render() { const { snippets } = this.state return (
-
    - { - snippets.map((snippet) => ( -
  • this.handleSnippetContextMenu(snippet)} - onClick={() => this.handleSnippetClick(snippet)}> - {snippet.name} -
  • - )) - } + {snippets.map(snippet => ( +
  • this.handleSnippetContextMenu(snippet)} + onClick={() => this.handleSnippetClick(snippet)} + > + {snippet.name} +
  • + ))}
) diff --git a/browser/main/modals/PreferencesModal/SnippetTab.js b/browser/main/modals/PreferencesModal/SnippetTab.js index df338d7f..0476c5c2 100644 --- a/browser/main/modals/PreferencesModal/SnippetTab.js +++ b/browser/main/modals/PreferencesModal/SnippetTab.js @@ -11,7 +11,7 @@ import copy from 'copy-to-clipboard' const path = require('path') class SnippetTab extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { currentSnippet: null @@ -19,7 +19,7 @@ class SnippetTab extends React.Component { this.changeDelay = null } - notify (title, options) { + notify(title, options) { if (global.process.platform === 'win32') { options.icon = path.join( 'file://', @@ -30,7 +30,7 @@ class SnippetTab extends React.Component { return new window.Notification(title, options) } - handleSnippetNameOrPrefixChange () { + handleSnippetNameOrPrefixChange() { clearTimeout(this.changeDelay) this.changeDelay = setTimeout(() => { // notify the snippet editor that the name or prefix of snippet has been changed @@ -39,20 +39,20 @@ class SnippetTab extends React.Component { }, 500) } - handleSnippetSelect (snippet) { + handleSnippetSelect(snippet) { const { currentSnippet } = this.state if (snippet !== null) { if (currentSnippet === null || currentSnippet.id !== snippet.id) { dataApi.fetchSnippet(snippet.id).then(changedSnippet => { // notify the snippet editor to load the content of the new snippet this.snippetEditor.onSnippetChanged(changedSnippet) - this.setState({currentSnippet: changedSnippet}) + this.setState({ currentSnippet: changedSnippet }) }) } } } - onSnippetNameOrPrefixChanged (e, type) { + onSnippetNameOrPrefixChanged(e, type) { const newSnippet = Object.assign({}, this.state.currentSnippet) if (type === 'name') { newSnippet.name = e.target.value @@ -63,14 +63,14 @@ class SnippetTab extends React.Component { this.handleSnippetNameOrPrefixChange() } - handleDeleteSnippet (snippet) { + handleDeleteSnippet(snippet) { // prevent old snippet still display when deleted if (snippet.id === this.state.currentSnippet.id) { - this.setState({currentSnippet: null}) + this.setState({ currentSnippet: null }) } } - handleCopySnippet (e) { + handleCopySnippet(e) { const showCopyNotification = this.props.config.ui.showCopyNotification copy(this.state.currentSnippet.content) if (showCopyNotification) { @@ -81,7 +81,7 @@ class SnippetTab extends React.Component { } } - render () { + render() { const { config, storageKey } = this.props const { currentSnippet } = this.state @@ -95,12 +95,19 @@ class SnippetTab extends React.Component { -
+ currentSnippet={currentSnippet} + /> +
-
@@ -110,18 +117,26 @@ class SnippetTab extends React.Component { { this.onSnippetNameOrPrefixChanged(e, 'name') }} - type='text' /> + onChange={e => { + this.onSnippetNameOrPrefixChanged(e, 'name') + }} + type='text' + />
-
{i18n.__('Snippet prefix')}
+
+ {i18n.__('Snippet prefix')} +
{ this.onSnippetNameOrPrefixChanged(e, 'prefix') }} - type='text' /> + onChange={e => { + this.onSnippetNameOrPrefixChanged(e, 'prefix') + }} + type='text' + />
@@ -140,7 +155,10 @@ class SnippetTab extends React.Component { matchingTriples={config.editor.matchingTriples} explodingPairs={config.editor.explodingPairs} scrollPastEnd={config.editor.scrollPastEnd} - onRef={ref => { this.snippetEditor = ref }} /> + onRef={ref => { + this.snippetEditor = ref + }} + />
@@ -148,7 +166,6 @@ class SnippetTab extends React.Component { } } -SnippetTab.PropTypes = { -} +SnippetTab.PropTypes = {} export default CSSModules(SnippetTab, styles) diff --git a/browser/main/modals/PreferencesModal/SnippetTab.styl b/browser/main/modals/PreferencesModal/SnippetTab.styl index 296f8167..83b0a296 100644 --- a/browser/main/modals/PreferencesModal/SnippetTab.styl +++ b/browser/main/modals/PreferencesModal/SnippetTab.styl @@ -140,66 +140,25 @@ body[data-theme="default"], body[data-theme="white"] .snippet-item-selected background darken($ui-backgroundColor, 5) -body[data-theme="dark"] - .snippets - background $ui-dark-backgroundColor - .snippet-item - color white - &::after - background $ui-dark-borderColor - &:hover - background darken($ui-dark-backgroundColor, 5) - .snippet-item-selected - background darken($ui-dark-backgroundColor, 5) - .snippet-detail - color white - .group-control-button - colorDarkPrimaryButton() +apply-theme(theme) + body[data-theme={theme}] + .snippets + background get-theme-var(theme, 'backgroundColor') + .snippet-item + color get-theme-var(theme, 'text-color') + &::after + background get-theme-var(theme, 'borderColor') + &:hover + background darken(get-theme-var(theme, 'backgroundColor'), 5) + .snippet-item-selected + background darken(get-theme-var(theme, 'backgroundColor'), 5) + .snippet-detail + color get-theme-var(theme, 'text-color') + .group-control-button + colorThemedPrimaryButton(theme) -body[data-theme="solarized-dark"] - .snippets - background $ui-solarized-dark-backgroundColor - .snippet-item - color white - &::after - background $ui-solarized-dark-borderColor - &:hover - background darken($ui-solarized-dark-backgroundColor, 5) - .snippet-item-selected - background darken($ui-solarized-dark-backgroundColor, 5) - .snippet-detail - color white - .group-control-button - colorSolarizedDarkPrimaryButton() +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) -body[data-theme="monokai"] - .snippets - background $ui-monokai-backgroundColor - .snippet-item - color White - &::after - background $ui-monokai-borderColor - &:hover - background darken($ui-monokai-backgroundColor, 5) - .snippet-item-selected - background darken($ui-monokai-backgroundColor, 5) - .snippet-detail - color white - .group-control-button - colorMonokaiPrimaryButton() - -body[data-theme="dracula"] - .snippets - background $ui-dracula-backgroundColor - .snippet-item - color #f8f8f2 - &::after - background $ui-dracula-borderColor - &:hover - background darken($ui-dracula-backgroundColor, 5) - .snippet-item-selected - background darken($ui-dracula-backgroundColor, 5) - .snippet-detail - color #f8f8f2 - .group-control-button - colorDraculaPrimaryButton() \ No newline at end of file +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/PreferencesModal/StorageItem.js b/browser/main/modals/PreferencesModal/StorageItem.js index 9af02962..3cb18e30 100644 --- a/browser/main/modals/PreferencesModal/StorageItem.js +++ b/browser/main/modals/PreferencesModal/StorageItem.js @@ -12,7 +12,7 @@ const { shell, remote } = require('electron') const { dialog } = remote class StorageItem extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -20,137 +20,156 @@ class StorageItem extends React.Component { } } - handleNewFolderButtonClick (e) { + handleNewFolderButtonClick(e) { const { storage } = this.props const input = { name: i18n.__('New Folder'), color: consts.FOLDER_COLORS[Math.floor(Math.random() * 7) % 7] } - dataApi.createFolder(storage.key, input) - .then((data) => { + dataApi + .createFolder(storage.key, input) + .then(data => { store.dispatch({ type: 'UPDATE_FOLDER', storage: data.storage }) }) - .catch((err) => { + .catch(err => { console.error(err) }) } - handleExternalButtonClick () { + handleExternalButtonClick() { const { storage } = this.props shell.showItemInFolder(storage.path) } - handleUnlinkButtonClick (e) { + handleUnlinkButtonClick(e) { const index = dialog.showMessageBox(remote.getCurrentWindow(), { type: 'warning', message: i18n.__('Unlink Storage'), - detail: i18n.__('Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.'), + detail: i18n.__( + 'Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.' + ), buttons: [i18n.__('Unlink'), i18n.__('Cancel')] }) if (index === 0) { const { storage } = this.props - dataApi.removeStorage(storage.key) + dataApi + .removeStorage(storage.key) .then(() => { store.dispatch({ type: 'REMOVE_STORAGE', storageKey: storage.key }) }) - .catch((err) => { + .catch(err => { throw err }) } } - handleLabelClick (e) { + handleLabelClick(e) { const { storage } = this.props - this.setState({ - isLabelEditing: true, - name: storage.name - }, () => { - this.refs.label.focus() - }) + this.setState( + { + isLabelEditing: true, + name: storage.name + }, + () => { + this.refs.label.focus() + } + ) } - handleLabelChange (e) { + handleLabelChange(e) { this.setState({ name: this.refs.label.value }) } - handleLabelBlur (e) { + handleLabelBlur(e) { const { storage } = this.props - dataApi - .renameStorage(storage.key, this.state.name) - .then((_storage) => { - store.dispatch({ - type: 'RENAME_STORAGE', - storage: _storage - }) - this.setState({ - isLabelEditing: false - }) + dataApi.renameStorage(storage.key, this.state.name).then(_storage => { + store.dispatch({ + type: 'RENAME_STORAGE', + storage: _storage }) + this.setState({ + isLabelEditing: false + }) + }) } - render () { + render() { const { storage, hostBoundingBox } = this.props return (
- {this.state.isLabelEditing - ?
- + this.handleLabelChange(e)} - onBlur={(e) => this.handleLabelBlur(e)} + onChange={e => this.handleLabelChange(e)} + onBlur={e => this.handleLabelBlur(e)} />
- :
this.handleLabelClick(e)} + ) : ( +
this.handleLabelClick(e)} > -   + +   {storage.name}  ({storage.path}) 
- } + )}
- - -
- +
) } diff --git a/browser/main/modals/PreferencesModal/StorageItem.styl b/browser/main/modals/PreferencesModal/StorageItem.styl index 3e588a17..03fa65eb 100644 --- a/browser/main/modals/PreferencesModal/StorageItem.styl +++ b/browser/main/modals/PreferencesModal/StorageItem.styl @@ -103,10 +103,17 @@ body[data-theme="solarized-dark"] background-color $ui-solarized-dark-button-backgroundColor color $ui-solarized-dark-text-color -body[data-theme="dracula"] - .header - border-color $ui-dracula-borderColor +apply-theme(theme) + body[data-theme={theme}] + .header + border-color get-theme-var(theme, 'borderColor') - .header-control-button - colorDraculaDefaultButton() - border-color $ui-dracula-borderColor \ No newline at end of file + .header-control-button + colorThemedPrimaryButton(theme) + border-color get-theme-var(theme, 'borderColor') + +for theme in 'dracula' + apply-theme(theme) + +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/main/modals/PreferencesModal/StoragesTab.js b/browser/main/modals/PreferencesModal/StoragesTab.js index e84fa88c..9df1a153 100644 --- a/browser/main/modals/PreferencesModal/StoragesTab.js +++ b/browser/main/modals/PreferencesModal/StoragesTab.js @@ -12,24 +12,27 @@ import fs from 'fs' const electron = require('electron') const { shell, remote } = electron -function browseFolder () { +function browseFolder() { const dialog = remote.dialog const defaultPath = remote.app.getPath('home') return new Promise((resolve, reject) => { - dialog.showOpenDialog({ - title: i18n.__('Select Directory'), - defaultPath, - properties: ['openDirectory', 'createDirectory'] - }, function (targetPaths) { - if (targetPaths == null) return resolve('') - resolve(targetPaths[0]) - }) + dialog.showOpenDialog( + { + title: i18n.__('Select Directory'), + defaultPath, + properties: ['openDirectory', 'createDirectory'] + }, + function(targetPaths) { + if (targetPaths == null) return resolve('') + resolve(targetPaths[0]) + } + ) }) } class StoragesTab extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -44,7 +47,7 @@ class StoragesTab extends React.Component { this.loadAttachmentStorage() } - loadAttachmentStorage () { + loadAttachmentStorage() { const promises = [] this.props.data.noteMap.map(note => { const promise = attachmentManagement.getAttachmentsPathAndStatus( @@ -58,106 +61,128 @@ class StoragesTab extends React.Component { Promise.all(promises) .then(data => { const result = data.reduce((acc, curr) => acc.concat(curr), []) - this.setState({attachments: result}) + this.setState({ attachments: result }) }) .catch(console.error) } - handleAddStorageButton (e) { - this.setState({ - page: 'ADD_STORAGE', - newStorage: { - name: 'Unnamed', - type: 'FILESYSTEM', - path: '' + handleAddStorageButton(e) { + this.setState( + { + page: 'ADD_STORAGE', + newStorage: { + name: 'Unnamed', + type: 'FILESYSTEM', + path: '' + } + }, + () => { + this.refs.addStorageName.select() } - }, () => { - this.refs.addStorageName.select() - }) + ) } - handleLinkClick (e) { + handleLinkClick(e) { shell.openExternal(e.currentTarget.href) e.preventDefault() } - handleRemoveUnusedAttachments (attachments) { - attachmentManagement.removeAttachmentsByPaths(attachments) + handleRemoveUnusedAttachments(attachments) { + attachmentManagement + .removeAttachmentsByPaths(attachments) .then(() => this.loadAttachmentStorage()) .catch(console.error) } - renderList () { + renderList() { const { data, boundingBox } = this.props const { attachments } = this.state - const unusedAttachments = attachments.filter(attachment => !attachment.isInUse) - const inUseAttachments = attachments.filter(attachment => attachment.isInUse) + const unusedAttachments = attachments.filter( + attachment => !attachment.isInUse + ) + const inUseAttachments = attachments.filter( + attachment => attachment.isInUse + ) const totalUnusedAttachments = unusedAttachments.length const totalInuseAttachments = inUseAttachments.length const totalAttachments = totalUnusedAttachments + totalInuseAttachments - const totalUnusedAttachmentsSize = unusedAttachments - .reduce((acc, curr) => { - const stats = fs.statSync(curr.path) - const fileSizeInBytes = stats.size - return acc + fileSizeInBytes - }, 0) - const totalInuseAttachmentsSize = inUseAttachments - .reduce((acc, curr) => { - const stats = fs.statSync(curr.path) - const fileSizeInBytes = stats.size - return acc + fileSizeInBytes - }, 0) - const totalAttachmentsSize = totalUnusedAttachmentsSize + totalInuseAttachmentsSize + const totalUnusedAttachmentsSize = unusedAttachments.reduce((acc, curr) => { + const stats = fs.statSync(curr.path) + const fileSizeInBytes = stats.size + return acc + fileSizeInBytes + }, 0) + const totalInuseAttachmentsSize = inUseAttachments.reduce((acc, curr) => { + const stats = fs.statSync(curr.path) + const fileSizeInBytes = stats.size + return acc + fileSizeInBytes + }, 0) + const totalAttachmentsSize = + totalUnusedAttachmentsSize + totalInuseAttachmentsSize - const unusedAttachmentPaths = unusedAttachments - .reduce((acc, curr) => acc.concat(curr.path), []) + const unusedAttachmentPaths = unusedAttachments.reduce( + (acc, curr) => acc.concat(curr.path), + [] + ) - if (!boundingBox) { return null } - const storageList = data.storageMap.map((storage) => { - return + if (!boundingBox) { + return null + } + const storageList = data.storageMap.map(storage => { + return ( + + ) }) return (
{i18n.__('Storage Locations')}
- {storageList.length > 0 - ? storageList - :
{i18n.__('No storage found.')}
- } + {storageList.length > 0 ? ( + storageList + ) : ( +
{i18n.__('No storage found.')}
+ )}
-
{i18n.__('Attachment storage')}

- Unused attachments size: {humanFileSize(totalUnusedAttachmentsSize)} ({totalUnusedAttachments} items) + Unused attachments size: {humanFileSize(totalUnusedAttachmentsSize)} ( + {totalUnusedAttachments} items)

- In use attachments size: {humanFileSize(totalInuseAttachmentsSize)} ({totalInuseAttachments} items) + In use attachments size: {humanFileSize(totalInuseAttachmentsSize)} ( + {totalInuseAttachments} items)

- Total attachments size: {humanFileSize(totalAttachmentsSize)} ({totalAttachments} items) + Total attachments size: {humanFileSize(totalAttachmentsSize)} ( + {totalAttachments} items)

-
) } - handleAddStorageBrowseButtonClick (e) { + handleAddStorageBrowseButtonClick(e) { browseFolder() - .then((targetPath) => { + .then(targetPath => { if (targetPath.length > 0) { const { newStorage } = this.state newStorage.path = targetPath @@ -166,13 +191,13 @@ class StoragesTab extends React.Component { }) } }) - .catch((err) => { + .catch(err => { console.error('BrowseFAILED') console.error(err) }) } - handleAddStorageChange (e) { + handleAddStorageChange(e) { const { newStorage } = this.state newStorage.name = this.refs.addStorageName.value newStorage.path = this.refs.addStoragePath.value @@ -181,13 +206,13 @@ class StoragesTab extends React.Component { }) } - handleAddStorageCreateButton (e) { + handleAddStorageCreateButton(e) { dataApi .addStorage({ name: this.state.newStorage.name, path: this.state.newStorage.path }) - .then((data) => { + .then(data => { const { dispatch } = this.props dispatch({ type: 'ADD_STORAGE', @@ -200,37 +225,39 @@ class StoragesTab extends React.Component { }) } - handleAddStorageCancelButton (e) { + handleAddStorageCancelButton(e) { this.setState({ page: 'LIST' }) } - renderAddStorage () { + renderAddStorage() { return (
-
{i18n.__('Add Storage')}
-
{i18n.__('Name')}
- this.handleAddStorageChange(e)} + onChange={e => this.handleAddStorageChange(e)} />
-
{i18n.__('Type')}
+
+ {i18n.__('Type')} +
-
-
{i18n.__('Location')} +
+ {i18n.__('Location')}
- this.handleAddStorageChange(e)} + onChange={e => this.handleAddStorageChange(e)} /> - @@ -264,21 +297,25 @@ class StoragesTab extends React.Component {
- - + +
-
-
) } - renderContent () { + renderContent() { switch (this.state.page) { case 'ADD_STORAGE': case 'ADD_FOLDER': @@ -289,12 +326,8 @@ class StoragesTab extends React.Component { } } - render () { - return ( -
- {this.renderContent()} -
- ) + render() { + return
{this.renderContent()}
} } diff --git a/browser/main/modals/PreferencesModal/StoragesTab.styl b/browser/main/modals/PreferencesModal/StoragesTab.styl index fbfa89e6..285e9c60 100644 --- a/browser/main/modals/PreferencesModal/StoragesTab.styl +++ b/browser/main/modals/PreferencesModal/StoragesTab.styl @@ -168,122 +168,49 @@ body[data-theme="dark"] .list-attachement-clear-button colorDarkPrimaryButton() -body[data-theme="solarized-dark"] - .root - color $ui-solarized-dark-text-color +apply-theme(theme) + body[data-theme={theme}] + .root + color get-theme-var(theme, 'text-color') - .folderList-item - border-bottom $ui-solarized-dark-borderColor + .folderList-item + border-bottom get-theme-var(theme, 'borderColor') - .folderList-empty - color $ui-solarized-dark-text-color + .folderList-empty + color get-theme-var(theme, 'text-color') - .list-empty - color $ui-solarized-dark-text-color - .list-control-addStorageButton - border-color $ui-solarized-dark-button-backgroundColor - background-color $ui-solarized-dark-button-backgroundColor - color $ui-solarized-dark-text-color + .list-empty + color get-theme-var(theme, 'text-color') + .list-control-addStorageButton + border-color get-theme-var(theme, 'button-backgroundColor') + background-color get-theme-var(theme, 'button-backgroundColor') + color get-theme-var(theme, 'text-color') - .addStorage-header - color $ui-solarized-dark-text-color - border-color $ui-solarized-dark-borderColor + .addStorage-header + color get-theme-var(theme, 'text-color') + border-color get-theme-var(theme, 'borderColor') - .addStorage-body-section-name-input - border-color $$ui-solarized-dark-borderColor + .addStorage-body-section-name-input + border-color $get-theme-var(theme, 'borderColor') - .addStorage-body-section-type-description - color $ui-solarized-dark-text-color + .addStorage-body-section-type-description + color get-theme-var(theme, 'text-color') - .addStorage-body-section-path-button - colorPrimaryButton() - .addStorage-body-control - border-color $ui-solarized-dark-borderColor + .addStorage-body-section-path-button + colorPrimaryButton() + .addStorage-body-control + border-color get-theme-var(theme, 'borderColor') - .addStorage-body-control-createButton - colorDarkPrimaryButton() - .addStorage-body-control-cancelButton - colorDarkDefaultButton() - border-color $ui-solarized-dark-borderColor - .list-attachement-clear-button - colorSolarizedDarkPrimaryButton() + .addStorage-body-control-createButton + colorDarkPrimaryButton() + .addStorage-body-control-cancelButton + colorDarkDefaultButton() + border-color get-theme-var(theme, 'borderColor') + .list-attachement-clear-button + colorThemedPrimaryButton(theme) -body[data-theme="monokai"] - .root - color $ui-monokai-text-color +for theme in 'solarized-dark' 'dracula' + apply-theme(theme) - .folderList-item - border-bottom $ui-monokai-borderColor - - .folderList-empty - color $ui-monokai-text-color - - .list-empty - color $ui-monokai-text-color - .list-control-addStorageButton - border-color $ui-monokai-button-backgroundColor - background-color $ui-monokai-button-backgroundColor - color $ui-monokai-text-color - - .addStorage-header - color $ui-monokai-text-color - border-color $ui-monokai-borderColor - - .addStorage-body-section-name-input - border-color $$ui-monokai-borderColor - - .addStorage-body-section-type-description - color $ui-monokai-text-color - - .addStorage-body-section-path-button - colorPrimaryButton() - .addStorage-body-control - border-color $ui-monokai-borderColor - - .addStorage-body-control-createButton - colorDarkPrimaryButton() - .addStorage-body-control-cancelButton - colorDarkDefaultButton() - border-color $ui-monokai-borderColor - .list-attachement-clear-button - colorMonokaiPrimaryButton() - -body[data-theme="dracula"] - .root - color $ui-dracula-text-color - - .folderList-item - border-bottom $ui-dracula-borderColor - - .folderList-empty - color $ui-dracula-text-color - - .list-empty - color $ui-dracula-text-color - .list-control-addStorageButton - border-color $ui-dracula-button-backgroundColor - background-color $ui-dracula-button-backgroundColor - color $ui-dracula-text-color - - .addStorage-header - color $ui-dracula-text-color - border-color $ui-dracula-borderColor - - .addStorage-body-section-name-input - border-color $$ui-dracula-borderColor - - .addStorage-body-section-type-description - color $ui-dracula-text-color - - .addStorage-body-section-path-button - colorPrimaryButton() - .addStorage-body-control - border-color $ui-dracula-borderColor - - .addStorage-body-control-createButton - colorDarkPrimaryButton() - .addStorage-body-control-cancelButton - colorDarkDefaultButton() - border-color $ui-dracula-borderColor - .list-attachement-clear-button - colorDraculaPrimaryButton() \ No newline at end of file +for theme in $themes + apply-theme(theme) diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index 29badea8..d9f90790 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -12,13 +12,15 @@ import _ from 'lodash' import i18n from 'browser/lib/i18n' import { getLanguages } from 'browser/lib/Languages' import normalizeEditorFontFamily from 'browser/lib/normalizeEditorFontFamily' +import uiThemes from 'browser/lib/ui-themes' + const OSX = global.process.platform === 'darwin' const electron = require('electron') const ipc = electron.ipcRenderer class UiTab extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { config: props.config, @@ -26,10 +28,16 @@ class UiTab extends React.Component { } } - componentDidMount () { - CodeMirror.autoLoadMode(this.codeMirrorInstance.getCodeMirror(), 'javascript') + componentDidMount() { + CodeMirror.autoLoadMode( + this.codeMirrorInstance.getCodeMirror(), + 'javascript' + ) CodeMirror.autoLoadMode(this.customCSSCM.getCodeMirror(), 'css') - CodeMirror.autoLoadMode(this.customMarkdownLintConfigCM.getCodeMirror(), 'javascript') + CodeMirror.autoLoadMode( + this.customMarkdownLintConfigCM.getCodeMirror(), + 'javascript' + ) CodeMirror.autoLoadMode(this.prettierConfigCM.getCodeMirror(), 'javascript') // Set CM editor Sizes this.customCSSCM.getCodeMirror().setSize('400px', '400px') @@ -37,16 +45,21 @@ class UiTab extends React.Component { this.customMarkdownLintConfigCM.getCodeMirror().setSize('400px', '200px') this.handleSettingDone = () => { - this.setState({UiAlert: { - type: 'success', - message: i18n.__('Successfully applied!') - }}) + this.setState({ + UiAlert: { + type: 'success', + message: i18n.__('Successfully applied!') + } + }) } - this.handleSettingError = (err) => { - this.setState({UiAlert: { - type: 'error', - message: err.message != null ? err.message : i18n.__('An error occurred!') - }}) + this.handleSettingError = err => { + this.setState({ + UiAlert: { + type: 'error', + message: + err.message != null ? err.message : i18n.__('An error occurred!') + } + }) } ipc.addListener('APP_SETTING_DONE', this.handleSettingDone) ipc.addListener('APP_SETTING_ERROR', this.handleSettingError) @@ -55,12 +68,12 @@ class UiTab extends React.Component { this.handleSlider(null, 2) } - componentWillUnmount () { + componentWillUnmount() { ipc.removeListener('APP_SETTING_DONE', this.handleSettingDone) ipc.removeListener('APP_SETTING_ERROR', this.handleSettingError) } - handleUIChange (e) { + handleUIChange(e) { const { codemirrorTheme } = this.state let checkHighLight = document.getElementById('checkHighLight') @@ -81,17 +94,18 @@ class UiTab extends React.Component { scheduleEnd: this.refs.scheduleEnd.value, language: this.refs.uiLanguage.value, defaultNote: this.refs.defaultNote.value, - tagNewNoteWithFilteringTags: this.refs.tagNewNoteWithFilteringTags.checked, + tagNewNoteWithFilteringTags: this.refs.tagNewNoteWithFilteringTags + .checked, showCopyNotification: this.refs.showCopyNotification.checked, confirmDeletion: this.refs.confirmDeletion.checked, showOnlyRelatedTags: this.refs.showOnlyRelatedTags.checked, showTagsAlphabetically: this.refs.showTagsAlphabetically.checked, saveTagsAlphabetically: this.refs.saveTagsAlphabetically.checked, enableLiveNoteCounts: this.refs.enableLiveNoteCounts.checked, + showScrollBar: this.refs.showScrollBar.checked, showMenuBar: this.refs.showMenuBar.checked, - disableDirectWrite: this.refs.uiD2w != null - ? this.refs.uiD2w.checked - : false + disableDirectWrite: + this.refs.uiD2w != null ? this.refs.uiD2w.checked : false }, editor: { theme: this.refs.editorTheme.value, @@ -117,7 +131,9 @@ class UiTab extends React.Component { spellcheck: this.refs.spellcheck.checked, enableSmartPaste: this.refs.enableSmartPaste.checked, enableMarkdownLint: this.refs.enableMarkdownLint.checked, - customMarkdownLintConfig: this.customMarkdownLintConfigCM.getCodeMirror().getValue(), + customMarkdownLintConfig: this.customMarkdownLintConfigCM + .getCodeMirror() + .getValue(), prettierConfig: this.prettierConfigCM.getCodeMirror().getValue(), deleteUnusedAttachments: this.refs.deleteUnusedAttachments.checked }, @@ -147,29 +163,34 @@ class UiTab extends React.Component { const newCodemirrorTheme = this.refs.editorTheme.value if (newCodemirrorTheme !== codemirrorTheme) { - const theme = consts.THEMES.find(theme => theme.name === newCodemirrorTheme) + const theme = consts.THEMES.find( + theme => theme.name === newCodemirrorTheme + ) if (theme) { checkHighLight.setAttribute('href', theme.path) } } - this.setState({ config: newConfig, codemirrorTheme: newCodemirrorTheme }, () => { - const {ui, editor, preview} = this.props.config - this.currentConfig = {ui, editor, preview} - if (_.isEqual(this.currentConfig, this.state.config)) { - this.props.haveToSave() - } else { - this.props.haveToSave({ - tab: 'UI', - type: 'warning', - message: i18n.__('Unsaved Changes!') - }) + this.setState( + { config: newConfig, codemirrorTheme: newCodemirrorTheme }, + () => { + const { ui, editor, preview } = this.props.config + this.currentConfig = { ui, editor, preview } + if (_.isEqual(this.currentConfig, this.state.config)) { + this.props.haveToSave() + } else { + this.props.haveToSave({ + tab: 'UI', + type: 'warning', + message: i18n.__('Unsaved Changes!') + }) + } } - }) + ) } - handleSaveUIClick (e) { + handleSaveUIClick(e) { const newConfig = { ui: this.state.config.ui, editor: this.state.config.editor, @@ -186,7 +207,7 @@ class UiTab extends React.Component { this.props.haveToSave() } - clearMessage () { + clearMessage() { _.debounce(() => { this.setState({ UiAlert: null @@ -232,15 +253,15 @@ class UiTab extends React.Component { render () { const UiAlert = this.state.UiAlert - const UiAlertElement = UiAlert != null - ?

- {UiAlert.message} -

- : null + const UiAlertElement = + UiAlert != null ? ( +

{UiAlert.message}

+ ) : null const themes = consts.THEMES const { config, codemirrorTheme } = this.state - const codemirrorSampleCode = 'function iamHappy (happy) {\n\tif (happy) {\n\t console.log("I am Happy!")\n\t} else {\n\t console.log("I am not Happy!")\n\t}\n};' + const codemirrorSampleCode = + 'function iamHappy (happy) {\n\tif (happy) {\n\t console.log("I am Happy!")\n\t} else {\n\t console.log("I am not Happy!")\n\t}\n};' const enableEditRulersStyle = config.editor.enableRulers ? 'block' : 'none' const fontFamily = normalizeEditorFontFamily(config.editor.fontFamily) return ( @@ -257,12 +278,30 @@ class UiTab extends React.Component { onChange={(e) => this.handleUIChange(e)} ref='uiTheme' > - - - - - - + + {uiThemes + .filter(theme => !theme.isDark) + .sort((a, b) => a.label.localeCompare(b.label)) + .map(theme => { + return ( + + ) + })} + + + {uiThemes + .filter(theme => theme.isDark) + .sort((a, b) => a.label.localeCompare(b.label)) + .map(theme => { + return ( + + ) + })} +
@@ -288,12 +327,30 @@ class UiTab extends React.Component { onChange={(e) => this.handleUIChange(e)} ref='uiScheduledTheme' > - - - - - - + + {uiThemes + .filter(theme => !theme.isDark) + .sort((a, b) => a.label.localeCompare(b.label)) + .map(theme => { + return ( + + ) + })} + + + {uiThemes + .filter(theme => theme.isDark) + .sort((a, b) => a.label.localeCompare(b.label)) + .map(theme => { + return ( + + ) + })} +
@@ -316,17 +373,18 @@ class UiTab extends React.Component {
-
- {i18n.__('Language')} -
+
{i18n.__('Language')}
- this.handleUIChange(e)} ref='uiLanguage' > - { - getLanguages().map((language) => ) - } + {getLanguages().map(language => ( + + ))}
@@ -336,12 +394,15 @@ class UiTab extends React.Component { {i18n.__('Default New Note')}
- this.handleUIChange(e)} ref='defaultNote' > - +
@@ -349,103 +410,133 @@ class UiTab extends React.Component {
- { - global.process.platform === 'win32' - ?
+ {global.process.platform === 'win32' ? ( +
- : null - } - -
Tags
- + ) : null}
+
+
Tags
+
+
@@ -453,21 +544,22 @@ class UiTab extends React.Component {
Editor
-
- {i18n.__('Editor Theme')} -
+
{i18n.__('Editor Theme')}
- -
+
(this.codeMirrorInstance = e)} value={codemirrorSampleCode} @@ -476,7 +568,8 @@ class UiTab extends React.Component { readOnly: true, mode: 'javascript', theme: codemirrorTheme - }} /> + }} + />
@@ -485,10 +578,11 @@ class UiTab extends React.Component { {i18n.__('Editor Font Size')}
- this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
@@ -498,10 +592,11 @@ class UiTab extends React.Component { {i18n.__('Editor Font Family')}
- this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
@@ -511,18 +606,21 @@ class UiTab extends React.Component { {i18n.__('Editor Indent Style')}
-   - this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} > @@ -536,23 +634,21 @@ class UiTab extends React.Component {
-
- this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
@@ -563,12 +659,15 @@ class UiTab extends React.Component { {i18n.__('Switch to Preview')}
-
@@ -579,15 +678,20 @@ class UiTab extends React.Component { {i18n.__('Editor Keymap')}
- -

{i18n.__('⚠️ Please restart boostnote after you change the keymap')}

+

+ {i18n.__( + '⚠️ Please restart boostnote after you change the keymap' + )} +

@@ -596,14 +700,21 @@ class UiTab extends React.Component { {i18n.__('Snippet Default Language')}
-
@@ -613,10 +724,11 @@ class UiTab extends React.Component { {i18n.__('Front matter title field')}
- this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
@@ -624,99 +736,119 @@ class UiTab extends React.Component {
@@ -725,10 +857,11 @@ class UiTab extends React.Component { {i18n.__('Matching character pairs')}
- this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
@@ -739,10 +872,11 @@ class UiTab extends React.Component { {i18n.__('Matching character triples')}
- this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
@@ -753,10 +887,11 @@ class UiTab extends React.Component { {i18n.__('Exploding character pairs')}
- this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
@@ -766,13 +901,22 @@ class UiTab extends React.Component { {i18n.__('Custom MarkdownLint Rules')}
- this.handleUIChange(e)} + this.handleUIChange(e)} checked={this.state.config.editor.enableMarkdownLint} ref='enableMarkdownLint' type='checkbox' - />  + /> +   {i18n.__('Enable MarkdownLint')} -
+
+ gutters: [ + 'CodeMirror-linenumbers', + 'CodeMirror-foldgutter', + 'CodeMirror-lint-markers' + ] + }} + />
@@ -796,10 +945,11 @@ class UiTab extends React.Component { {i18n.__('Preview Font Size')}
- this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
@@ -809,124 +959,152 @@ class UiTab extends React.Component { {i18n.__('Preview Font Family')}
- this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
-
{i18n.__('Code Block Theme')}
+
+ {i18n.__('Code Block Theme')} +
-
-
- {i18n.__('Sanitization')} -
+
{i18n.__('Sanitization')}
-
@@ -935,10 +1113,11 @@ class UiTab extends React.Component { {i18n.__('LaTeX Inline Open Delimiter')}
- this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
@@ -948,10 +1127,11 @@ class UiTab extends React.Component { {i18n.__('LaTeX Inline Close Delimiter')}
- this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
@@ -961,10 +1141,11 @@ class UiTab extends React.Component { {i18n.__('LaTeX Block Open Delimiter')}
- this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
@@ -974,10 +1155,11 @@ class UiTab extends React.Component { {i18n.__('LaTeX Block Close Delimiter')}
- this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
@@ -987,26 +1169,27 @@ class UiTab extends React.Component { {i18n.__('PlantUML Server')}
- this.handleUIChange(e)} + onChange={e => this.handleUIChange(e)} type='text' />
-
- {i18n.__('Custom CSS')} -
+
{i18n.__('Custom CSS')}
- this.handleUIChange(e)} + this.handleUIChange(e)} checked={config.preview.allowCustomCSS} ref='previewAllowCustomCSS' type='checkbox' - />  + /> +   {i18n.__('Allow custom CSS for preview')} -
+
+ }} + />
@@ -1026,7 +1210,7 @@ class UiTab extends React.Component { {i18n.__('Prettier Config')}
-
+
+ }} + />
- {UiAlertElement}
diff --git a/browser/main/modals/PreferencesModal/index.js b/browser/main/modals/PreferencesModal/index.js index 86957083..2c14e6c7 100644 --- a/browser/main/modals/PreferencesModal/index.js +++ b/browser/main/modals/PreferencesModal/index.js @@ -16,7 +16,7 @@ import _ from 'lodash' import i18n from 'browser/lib/i18n' class Preferences extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -27,44 +27,39 @@ class Preferences extends React.Component { } } - componentDidMount () { + componentDidMount() { this.refs.root.focus() const boundingBox = this.getContentBoundingBox() this.setState({ boundingBox }) } - switchTeam (teamId) { - this.setState({currentTeamId: teamId}) + switchTeam(teamId) { + this.setState({ currentTeamId: teamId }) } - handleNavButtonClick (tab) { - return (e) => { - this.setState({currentTab: tab}) + handleNavButtonClick(tab) { + return e => { + this.setState({ currentTab: tab }) } } - handleEscButtonClick () { + handleEscButtonClick() { this.props.close() } - renderContent () { + renderContent() { const { boundingBox } = this.state const { dispatch, config, data } = this.props switch (this.state.currentTab) { case 'INFO': - return ( - - ) + return case 'HOTKEY': return ( this.setState({HotkeyAlert: alert})} + haveToSave={alert => this.setState({ HotkeyAlert: alert })} /> ) case 'UI': @@ -72,29 +67,21 @@ class Preferences extends React.Component { this.setState({UIAlert: alert})} + haveToSave={alert => this.setState({ UIAlert: alert })} /> ) case 'CROWDFUNDING': - return ( - - ) + return case 'BLOG': return ( this.setState({BlogAlert: alert})} + haveToSave={alert => this.setState({ BlogAlert: alert })} /> ) case 'SNIPPET': - return ( - - ) + return case 'STORAGES': default: return ( @@ -107,67 +94,69 @@ class Preferences extends React.Component { } } - handleKeyDown (e) { + handleKeyDown(e) { if (e.keyCode === 27) { this.props.close() } } - getContentBoundingBox () { + getContentBoundingBox() { return this.refs.content.getBoundingClientRect() } - haveToSaveNotif (type, message) { - return ( -

{message}

- ) + haveToSaveNotif(type, message) { + return

{message}

} - render () { + render() { const content = this.renderContent() const tabs = [ - {target: 'STORAGES', label: i18n.__('Storage')}, - {target: 'HOTKEY', label: i18n.__('Hotkeys'), Hotkey: this.state.HotkeyAlert}, - {target: 'UI', label: i18n.__('Interface'), UI: this.state.UIAlert}, - {target: 'INFO', label: i18n.__('About')}, - {target: 'CROWDFUNDING', label: i18n.__('Crowdfunding')}, - {target: 'BLOG', label: i18n.__('Blog'), Blog: this.state.BlogAlert}, - {target: 'SNIPPET', label: i18n.__('Snippets')} + { target: 'STORAGES', label: i18n.__('Storage') }, + { + target: 'HOTKEY', + label: i18n.__('Hotkeys'), + Hotkey: this.state.HotkeyAlert + }, + { target: 'UI', label: i18n.__('Interface'), UI: this.state.UIAlert }, + { target: 'INFO', label: i18n.__('About') }, + { target: 'CROWDFUNDING', label: i18n.__('Crowdfunding') }, + { target: 'BLOG', label: i18n.__('Blog'), Blog: this.state.BlogAlert }, + { target: 'SNIPPET', label: i18n.__('Snippets') } ] - const navButtons = tabs.map((tab) => { + const navButtons = tabs.map(tab => { const isActive = this.state.currentTab === tab.target - const isUiHotkeyTab = _.isObject(tab[tab.label]) && tab.label === tab[tab.label].tab + const isUiHotkeyTab = + _.isObject(tab[tab.label]) && tab.label === tab[tab.label].tab return ( - ) }) return ( -
this.handleKeyDown(e)} + onKeyDown={e => this.handleKeyDown(e)} >

{i18n.__('Your preferences for Boostnote')}

- this.handleEscButtonClick(e)} /> -
- {navButtons} -
+ this.handleEscButtonClick(e)} + /> +
{navButtons}
{content}
@@ -181,4 +170,4 @@ Preferences.propTypes = { dispatch: PropTypes.func } -export default connect((x) => x)(CSSModules(Preferences, styles)) +export default connect(x => x)(CSSModules(Preferences, styles)) diff --git a/browser/main/modals/RenameFolderModal.js b/browser/main/modals/RenameFolderModal.js index 9fdd70c8..a8d6f386 100644 --- a/browser/main/modals/RenameFolderModal.js +++ b/browser/main/modals/RenameFolderModal.js @@ -8,7 +8,7 @@ import ModalEscButton from 'browser/components/ModalEscButton' import i18n from 'browser/lib/i18n' class RenameFolderModal extends React.Component { - constructor (props) { + constructor(props) { super(props) this.state = { @@ -16,39 +16,39 @@ class RenameFolderModal extends React.Component { } } - componentDidMount () { + componentDidMount() { this.refs.name.focus() this.refs.name.select() } - handleCloseButtonClick (e) { + handleCloseButtonClick(e) { this.props.close() } - handleChange (e) { + handleChange(e) { this.setState({ name: this.refs.name.value }) } - handleKeyDown (e) { + handleKeyDown(e) { if (e.keyCode === 27) { this.props.close() } } - handleInputKeyDown (e) { + handleInputKeyDown(e) { switch (e.keyCode) { case 13: this.confirm() } } - handleConfirmButtonClick (e) { + handleConfirmButtonClick(e) { this.confirm() } - confirm () { + confirm() { if (this.state.name.trim().length > 0) { const { storage, folder } = this.props dataApi @@ -56,7 +56,7 @@ class RenameFolderModal extends React.Component { name: this.state.name, color: folder.color }) - .then((data) => { + .then(data => { store.dispatch({ type: 'UPDATE_FOLDER', storage: data.storage @@ -66,27 +66,32 @@ class RenameFolderModal extends React.Component { } } - render () { + render() { return ( -
this.handleKeyDown(e)} + onKeyDown={e => this.handleKeyDown(e)} >
{i18n.__('Rename Folder')}
- this.handleCloseButtonClick(e)} /> + this.handleCloseButtonClick(e)} + />
- this.handleChange(e)} - onKeyDown={(e) => this.handleInputKeyDown(e)} + onChange={e => this.handleChange(e)} + onKeyDown={e => this.handleInputKeyDown(e)} /> - diff --git a/browser/main/modals/RenameFolderModal.styl b/browser/main/modals/RenameFolderModal.styl index c9909d00..435aa6a0 100644 --- a/browser/main/modals/RenameFolderModal.styl +++ b/browser/main/modals/RenameFolderModal.styl @@ -43,23 +43,31 @@ border-radius 2px padding 0 25px margin 0 auto + font-size 14px colorPrimaryButton() -body[data-theme="dark"] - .root - modalDark() +apply-theme(theme) + body[data-theme={theme}] + .root + background-color transparent - .header - background-color $ui-dark-button--hover-backgroundColor - border-color $ui-dark-borderColor - color $ui-dark-text-color + .header + background-color get-theme-var(theme, 'button--hover-backgroundColor') + border-color get-theme-var(theme, 'borderColor') + color get-theme-var(theme, 'text-color') - .description - color $ui-inactive-text-color + .description + color $ui-inactive-text-color - .control-input - border-color $ui-dark-borderColor - color $ui-dark-text-color + .control-input + border-color get-theme-var(theme, 'borderColor') + color get-theme-var(theme, 'text-color') - .control-confirmButton - colorDarkPrimaryButton() + .control-confirmButton + colorThemedPrimaryButton(theme) + +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) + +for theme in $themes + apply-theme(theme) diff --git a/browser/main/store.js b/browser/main/store.js index d48198a7..d48946a6 100644 --- a/browser/main/store.js +++ b/browser/main/store.js @@ -6,7 +6,7 @@ import { Map, Set } from 'browser/lib/Mutable' import _ from 'lodash' import DevTools from './DevTools' -function defaultDataMap () { +function defaultDataMap() { return { storageMap: new Map(), noteMap: new Map(), @@ -18,16 +18,16 @@ function defaultDataMap () { } } -function data (state = defaultDataMap(), action) { +function data(state = defaultDataMap(), action) { switch (action.type) { case 'INIT_ALL': state = defaultDataMap() - action.storages.forEach((storage) => { + action.storages.forEach(storage => { state.storageMap.set(storage.key, storage) }) - action.notes.some((note) => { + action.notes.some(note => { if (note === undefined) return true const uniqueKey = note.key const folderKey = note.storage + '-' + note.folder @@ -40,7 +40,10 @@ function data (state = defaultDataMap(), action) { if (note.isTrashed) { state.trashedSet.add(uniqueKey) } - const storageNoteList = getOrInitItem(state.storageNoteMap, note.storage) + const storageNoteList = getOrInitItem( + state.storageNoteMap, + note.storage + ) storageNoteList.add(uniqueKey) const folderNoteSet = getOrInitItem(state.folderNoteMap, folderKey) @@ -51,173 +54,170 @@ function data (state = defaultDataMap(), action) { } }) return state - case 'UPDATE_NOTE': - { - const note = action.note - const uniqueKey = note.key - const folderKey = note.storage + '-' + note.folder - const oldNote = state.noteMap.get(uniqueKey) + case 'UPDATE_NOTE': { + const note = action.note + const uniqueKey = note.key + const folderKey = note.storage + '-' + note.folder + const oldNote = state.noteMap.get(uniqueKey) - state = Object.assign({}, state) - state.noteMap = new Map(state.noteMap) - state.noteMap.set(uniqueKey, note) + state = Object.assign({}, state) + state.noteMap = new Map(state.noteMap) + state.noteMap.set(uniqueKey, note) - updateStarredChange(oldNote, note, state, uniqueKey) + updateStarredChange(oldNote, note, state, uniqueKey) - if (oldNote == null || oldNote.isTrashed !== note.isTrashed) { - state.trashedSet = new Set(state.trashedSet) - if (note.isTrashed) { - state.trashedSet.add(uniqueKey) - state.starredSet.delete(uniqueKey) - removeFromTags(note.tags, state, uniqueKey) - } else { - state.trashedSet.delete(uniqueKey) - - assignToTags(note.tags, state, uniqueKey) - - if (note.isStarred) { - state.starredSet.add(uniqueKey) - } - } - } - - // Update storageNoteMap if oldNote doesn't exist - if (oldNote == null) { - state.storageNoteMap = new Map(state.storageNoteMap) - let storageNoteSet = state.storageNoteMap.get(note.storage) - storageNoteSet = new Set(storageNoteSet) - storageNoteSet.add(uniqueKey) - state.storageNoteMap.set(note.storage, storageNoteSet) - } - - // Update foldermap if folder changed or post created - updateFolderChange(oldNote, note, state, folderKey, uniqueKey) - - if (oldNote != null) { - updateTagChanges(oldNote, note, state, uniqueKey) + if (oldNote == null || oldNote.isTrashed !== note.isTrashed) { + state.trashedSet = new Set(state.trashedSet) + if (note.isTrashed) { + state.trashedSet.add(uniqueKey) + state.starredSet.delete(uniqueKey) + removeFromTags(note.tags, state, uniqueKey) } else { + state.trashedSet.delete(uniqueKey) + assignToTags(note.tags, state, uniqueKey) - } - return state + if (note.isStarred) { + state.starredSet.add(uniqueKey) + } + } } - case 'MOVE_NOTE': - { - const originNote = action.originNote - const originKey = originNote.key - const note = action.note - const uniqueKey = note.key - const folderKey = note.storage + '-' + note.folder - const oldNote = state.noteMap.get(uniqueKey) - state = Object.assign({}, state) - state.noteMap = new Map(state.noteMap) - state.noteMap.delete(originKey) - state.noteMap.set(uniqueKey, note) + // Update storageNoteMap if oldNote doesn't exist + if (oldNote == null) { + state.storageNoteMap = new Map(state.storageNoteMap) + let storageNoteSet = state.storageNoteMap.get(note.storage) + storageNoteSet = new Set(storageNoteSet) + storageNoteSet.add(uniqueKey) + state.storageNoteMap.set(note.storage, storageNoteSet) + } - // If storage chanced, origin key must be discarded - if (originKey !== uniqueKey) { - // From isStarred - if (originNote.isStarred) { - state.starredSet = new Set(state.starredSet) - state.starredSet.delete(originKey) - } + // Update foldermap if folder changed or post created + updateFolderChange(oldNote, note, state, folderKey, uniqueKey) - if (originNote.isTrashed) { - state.trashedSet = new Set(state.trashedSet) - state.trashedSet.delete(originKey) - } + if (oldNote != null) { + updateTagChanges(oldNote, note, state, uniqueKey) + } else { + assignToTags(note.tags, state, uniqueKey) + } - // From storageNoteMap - state.storageNoteMap = new Map(state.storageNoteMap) - let noteSet = state.storageNoteMap.get(originNote.storage) - noteSet = new Set(noteSet) - noteSet.delete(originKey) - state.storageNoteMap.set(originNote.storage, noteSet) + return state + } + case 'MOVE_NOTE': { + const originNote = action.originNote + const originKey = originNote.key + const note = action.note + const uniqueKey = note.key + const folderKey = note.storage + '-' + note.folder + const oldNote = state.noteMap.get(uniqueKey) - // From folderNoteMap - state.folderNoteMap = new Map(state.folderNoteMap) - const originFolderKey = originNote.storage + '-' + originNote.folder - let originFolderList = state.folderNoteMap.get(originFolderKey) - originFolderList = new Set(originFolderList) - originFolderList.delete(originKey) - state.folderNoteMap.set(originFolderKey, originFolderList) + state = Object.assign({}, state) + state.noteMap = new Map(state.noteMap) + state.noteMap.delete(originKey) + state.noteMap.set(uniqueKey, note) - removeFromTags(originNote.tags, state, originKey) + // If storage chanced, origin key must be discarded + if (originKey !== uniqueKey) { + // From isStarred + if (originNote.isStarred) { + state.starredSet = new Set(state.starredSet) + state.starredSet.delete(originKey) } - updateStarredChange(oldNote, note, state, uniqueKey) - - if (oldNote == null || oldNote.isTrashed !== note.isTrashed) { + if (originNote.isTrashed) { state.trashedSet = new Set(state.trashedSet) - if (note.isTrashed) { - state.trashedSet.add(uniqueKey) - } else { - state.trashedSet.delete(uniqueKey) - } + state.trashedSet.delete(originKey) } - // Update storageNoteMap if oldNote doesn't exist - if (oldNote == null) { - state.storageNoteMap = new Map(state.storageNoteMap) - let noteSet = state.storageNoteMap.get(note.storage) - noteSet = new Set(noteSet) - noteSet.add(uniqueKey) - state.storageNoteMap.set(folderKey, noteSet) - } - - // Update foldermap if folder changed or post created - updateFolderChange(oldNote, note, state, folderKey, uniqueKey) - - // Remove from old folder map - if (oldNote != null) { - updateTagChanges(oldNote, note, state, uniqueKey) - } else { - assignToTags(note.tags, state, uniqueKey) - } - - return state - } - case 'DELETE_NOTE': - { - const uniqueKey = action.noteKey - const targetNote = state.noteMap.get(uniqueKey) - - state = Object.assign({}, state) - // From storageNoteMap state.storageNoteMap = new Map(state.storageNoteMap) - let noteSet = state.storageNoteMap.get(targetNote.storage) + let noteSet = state.storageNoteMap.get(originNote.storage) noteSet = new Set(noteSet) - noteSet.delete(uniqueKey) - state.storageNoteMap.set(targetNote.storage, noteSet) + noteSet.delete(originKey) + state.storageNoteMap.set(originNote.storage, noteSet) - if (targetNote != null) { - // From isStarred - if (targetNote.isStarred) { - state.starredSet = new Set(state.starredSet) - state.starredSet.delete(uniqueKey) - } + // From folderNoteMap + state.folderNoteMap = new Map(state.folderNoteMap) + const originFolderKey = originNote.storage + '-' + originNote.folder + let originFolderList = state.folderNoteMap.get(originFolderKey) + originFolderList = new Set(originFolderList) + originFolderList.delete(originKey) + state.folderNoteMap.set(originFolderKey, originFolderList) - if (targetNote.isTrashed) { - state.trashedSet = new Set(state.trashedSet) - state.trashedSet.delete(uniqueKey) - } - - // From folderNoteMap - const folderKey = targetNote.storage + '-' + targetNote.folder - state.folderNoteMap = new Map(state.folderNoteMap) - let folderSet = state.folderNoteMap.get(folderKey) - folderSet = new Set(folderSet) - folderSet.delete(uniqueKey) - state.folderNoteMap.set(folderKey, folderSet) - - removeFromTags(targetNote.tags, state, uniqueKey) - } - state.noteMap = new Map(state.noteMap) - state.noteMap.delete(uniqueKey) - return state + removeFromTags(originNote.tags, state, originKey) } + + updateStarredChange(oldNote, note, state, uniqueKey) + + if (oldNote == null || oldNote.isTrashed !== note.isTrashed) { + state.trashedSet = new Set(state.trashedSet) + if (note.isTrashed) { + state.trashedSet.add(uniqueKey) + } else { + state.trashedSet.delete(uniqueKey) + } + } + + // Update storageNoteMap if oldNote doesn't exist + if (oldNote == null) { + state.storageNoteMap = new Map(state.storageNoteMap) + let noteSet = state.storageNoteMap.get(note.storage) + noteSet = new Set(noteSet) + noteSet.add(uniqueKey) + state.storageNoteMap.set(folderKey, noteSet) + } + + // Update foldermap if folder changed or post created + updateFolderChange(oldNote, note, state, folderKey, uniqueKey) + + // Remove from old folder map + if (oldNote != null) { + updateTagChanges(oldNote, note, state, uniqueKey) + } else { + assignToTags(note.tags, state, uniqueKey) + } + + return state + } + case 'DELETE_NOTE': { + const uniqueKey = action.noteKey + const targetNote = state.noteMap.get(uniqueKey) + + state = Object.assign({}, state) + + // From storageNoteMap + state.storageNoteMap = new Map(state.storageNoteMap) + let noteSet = state.storageNoteMap.get(targetNote.storage) + noteSet = new Set(noteSet) + noteSet.delete(uniqueKey) + state.storageNoteMap.set(targetNote.storage, noteSet) + + if (targetNote != null) { + // From isStarred + if (targetNote.isStarred) { + state.starredSet = new Set(state.starredSet) + state.starredSet.delete(uniqueKey) + } + + if (targetNote.isTrashed) { + state.trashedSet = new Set(state.trashedSet) + state.trashedSet.delete(uniqueKey) + } + + // From folderNoteMap + const folderKey = targetNote.storage + '-' + targetNote.folder + state.folderNoteMap = new Map(state.folderNoteMap) + let folderSet = state.folderNoteMap.get(folderKey) + folderSet = new Set(folderSet) + folderSet.delete(uniqueKey) + state.folderNoteMap.set(folderKey, folderSet) + + removeFromTags(targetNote.tags, state, uniqueKey) + } + state.noteMap = new Map(state.noteMap) + state.noteMap.delete(uniqueKey) + return state + } case 'UPDATE_FOLDER': case 'REORDER_FOLDER': case 'EXPORT_FOLDER': @@ -247,7 +247,7 @@ function data (state = defaultDataMap(), action) { state.storageNoteMap.set(action.storage.key, storageNoteSet) if (noteSet != null) { - noteSet.forEach(function handleNoteKey (noteKey) { + noteSet.forEach(function handleNoteKey(noteKey) { // Get note from noteMap const note = state.noteMap.get(noteKey) if (note != null) { @@ -269,7 +269,7 @@ function data (state = defaultDataMap(), action) { // Delete key from tag map state.tagNoteMap = new Map(state.tagNoteMap) - note.tags.forEach((tag) => { + note.tags.forEach(tag => { const tagNoteSet = getOrInitItem(state.tagNoteMap, tag) tagNoteSet.delete(noteKey) }) @@ -288,7 +288,7 @@ function data (state = defaultDataMap(), action) { state.storageNoteMap.set(action.storage.key, new Set()) state.folderNoteMap = new Map(state.folderNoteMap) state.tagNoteMap = new Map(state.tagNoteMap) - action.notes.forEach((note) => { + action.notes.forEach(note => { const uniqueKey = note.key const folderKey = note.storage + '-' + note.folder state.noteMap.set(uniqueKey, note) @@ -307,7 +307,7 @@ function data (state = defaultDataMap(), action) { } folderNoteSet.add(uniqueKey) - note.tags.forEach((tag) => { + note.tags.forEach(tag => { const tagNoteSet = getOrInitItem(state.tagNoteMap, tag) tagNoteSet.add(uniqueKey) }) @@ -322,7 +322,7 @@ function data (state = defaultDataMap(), action) { // Remove folders from folderMap if (storage != null) { state.folderMap = new Map(state.folderMap) - storage.folders.forEach((folder) => { + storage.folders.forEach(folder => { const folderKey = storage.key + '-' + folder.key state.folderMap.delete(folderKey) }) @@ -334,17 +334,17 @@ function data (state = defaultDataMap(), action) { state.storageNoteMap.delete(action.storageKey) if (storageNoteSet != null) { const notes = storageNoteSet - .map((noteKey) => state.noteMap.get(noteKey)) - .filter((note) => note != null) + .map(noteKey => state.noteMap.get(noteKey)) + .filter(note => note != null) state.noteMap = new Map(state.noteMap) state.tagNoteMap = new Map(state.tagNoteMap) state.starredSet = new Set(state.starredSet) - notes.forEach((note) => { + notes.forEach(note => { const noteKey = note.key state.noteMap.delete(noteKey) state.starredSet.delete(noteKey) - note.tags.forEach((tag) => { + note.tags.forEach(tag => { let tagNoteSet = state.tagNoteMap.get(tag) tagNoteSet = new Set(tagNoteSet) tagNoteSet.delete(noteKey) @@ -364,7 +364,7 @@ function data (state = defaultDataMap(), action) { const defaultConfig = ConfigManager.get() -function config (state = defaultConfig, action) { +function config(state = defaultConfig, action) { switch (action.type) { case 'SET_IS_SIDENAV_FOLDED': state.isSideNavFolded = action.isFolded @@ -390,7 +390,7 @@ const defaultStatus = { updateReady: false } -function status (state = defaultStatus, action) { +function status(state = defaultStatus, action) { switch (action.type) { case 'UPDATE_AVAILABLE': return Object.assign({}, defaultStatus, { @@ -400,7 +400,7 @@ function status (state = defaultStatus, action) { return state } -function updateStarredChange (oldNote, note, state, uniqueKey) { +function updateStarredChange(oldNote, note, state, uniqueKey) { if (oldNote == null || oldNote.isStarred !== note.isStarred) { state.starredSet = new Set(state.starredSet) if (note.isStarred) { @@ -411,7 +411,7 @@ function updateStarredChange (oldNote, note, state, uniqueKey) { } } -function updateFolderChange (oldNote, note, state, folderKey, uniqueKey) { +function updateFolderChange(oldNote, note, state, folderKey, uniqueKey) { if (oldNote == null || oldNote.folder !== note.folder) { state.folderNoteMap = new Map(state.folderNoteMap) let folderNoteList = state.folderNoteMap.get(folderKey) @@ -429,7 +429,7 @@ function updateFolderChange (oldNote, note, state, folderKey, uniqueKey) { } } -function updateTagChanges (oldNote, note, state, uniqueKey) { +function updateTagChanges(oldNote, note, state, uniqueKey) { const discardedTags = _.difference(oldNote.tags, note.tags) const addedTags = _.difference(note.tags, oldNote.tags) if (discardedTags.length + addedTags.length > 0) { @@ -438,15 +438,15 @@ function updateTagChanges (oldNote, note, state, uniqueKey) { } } -function assignToTags (tags, state, uniqueKey) { +function assignToTags(tags, state, uniqueKey) { state.tagNoteMap = new Map(state.tagNoteMap) - tags.forEach((tag) => { + tags.forEach(tag => { const tagNoteList = getOrInitItem(state.tagNoteMap, tag) tagNoteList.add(uniqueKey) }) } -function removeFromTags (tags, state, uniqueKey) { +function removeFromTags(tags, state, uniqueKey) { state.tagNoteMap = new Map(state.tagNoteMap) tags.forEach(tag => { let tagNoteList = state.tagNoteMap.get(tag) @@ -458,7 +458,7 @@ function removeFromTags (tags, state, uniqueKey) { }) } -function getOrInitItem (target, key) { +function getOrInitItem(target, key) { let results = target.get(key) if (results == null) { results = new Set() @@ -476,8 +476,15 @@ const reducer = combineReducers({ router: connectRouter(history) }) -const store = createStore(reducer, undefined, process.env.NODE_ENV === 'development' - ? compose(applyMiddleware(routerMiddleware(history)), DevTools.instrument()) - : applyMiddleware(routerMiddleware(history))) +const store = createStore( + reducer, + undefined, + process.env.NODE_ENV === 'development' + ? compose( + applyMiddleware(routerMiddleware(history)), + DevTools.instrument() + ) + : applyMiddleware(routerMiddleware(history)) +) export { store, history } diff --git a/browser/styles/Detail/TagSelect.styl b/browser/styles/Detail/TagSelect.styl index 8900422c..5fc23310 100644 --- a/browser/styles/Detail/TagSelect.styl +++ b/browser/styles/Detail/TagSelect.styl @@ -51,78 +51,34 @@ background-color alpha($ui-button--active-backgroundColor, 40%) color $ui-text-color -body[data-theme="dark"] - .TagSelect - .react-autosuggest__input - color $ui-dark-text-color - - ul - border-color $ui-dark-borderColor - background-color $ui-dark-noteList-backgroundColor - color $ui-dark-text-color - - &:before - background-color $ui-dark-noteList-backgroundColor - - li[aria-selected="true"] - background-color $ui-dark-button--active-backgroundColor - color $ui-dark-text-color - -body[data-theme="monokai"] - .TagSelect - .react-autosuggest__input - color $ui-monokai-text-color - - ul - border-color $ui-monokai-borderColor - background-color $ui-monokai-noteList-backgroundColor - color $ui-monokai-text-color - - &:before - background-color $ui-dark-noteList-backgroundColor - - li[aria-selected="true"] - background-color $ui-monokai-button-backgroundColor - color $ui-monokai-text-color - -body[data-theme="dracula"] - .TagSelect - .react-autosuggest__input - color $ui-dracula-text-color - - ul - border-color $ui-dracula-borderColor - background-color $ui-dracula-noteList-backgroundColor - color $ui-dracula-text-color - - &:before - background-color $ui-dark-noteList-backgroundColor - - li[aria-selected="true"] - background-color $ui-dracula-button-backgroundColor - color $ui-dracula-text-color - -body[data-theme="solarized-dark"] - .TagSelect - .react-autosuggest__input - color $ui-solarized-dark-text-color - - ul - border-color $ui-solarized-dark-borderColor - background-color $ui-solarized-dark-noteList-backgroundColor - color $ui-solarized-dark-text-color - - &:before - background-color $ui-solarized-dark-noteList-backgroundColor - - li[aria-selected="true"] - background-color $ui-dark-button--active-backgroundColor - color $ui-solarized-dark-text-color - body[data-theme="white"] .TagSelect ul background-color $ui-white-noteList-backgroundColor li[aria-selected="true"] - background-color $ui-button--active-backgroundColor \ No newline at end of file + background-color $ui-button--active-backgroundColor + +apply-theme(theme) + body[data-theme={theme}] + .TagSelect + .react-autosuggest__input + color get-theme-var(theme, 'text-color') + + ul + border-color get-theme-var(theme, 'borderColor') + background-color get-theme-var(theme, 'noteList-backgroundColor') + color get-theme-var(theme, 'text-color') + + &:before + background-color $ui-dark-noteList-backgroundColor + + li[aria-selected="true"] + background-color get-theme-var(theme, 'button-backgroundColor') + color get-theme-var(theme, 'text-color') + +for theme in 'dark' 'solarized-dark' 'dracula' + apply-theme(theme) + +for theme in $themes + apply-theme(theme) \ No newline at end of file diff --git a/browser/styles/index.styl b/browser/styles/index.styl index 53456da9..2338938f 100644 --- a/browser/styles/index.styl +++ b/browser/styles/index.styl @@ -107,36 +107,18 @@ colorDarkPrimaryButton() &:active:hover background-color $dark-primary-button-background--active - -colorSolarizedDarkPrimaryButton() - color $ui-solarized-dark-text-color - background-color $ui-solarized-dark-button-backgroundColor - border none - &:hover - background-color $dark-primary-button-background--hover - &:active - &:active:hover - background-color $dark-primary-button-background--active - -colorMonokaiPrimaryButton() - color $ui-monokai-text-color - background-color $ui-monokai-button-backgroundColor - border none - &:hover - background-color $dark-primary-button-background--hover - &:active - &:active:hover - background-color $dark-primary-button-background--active - -colorDraculaPrimaryButton() - color $ui-dracula-text-color - background-color $ui-dracula-button-backgroundColor - border none - &:hover - background-color $ui-dracula-button--active-backgroundColor - &:active - &:active:hover - background-color $ui-dracula-button--active-backgroundColor +colorThemedPrimaryButton(theme) + if theme == 'dark' + colorDarkPrimaryButton() + else + color get-theme-var(theme, 'text-color') + background-color get-theme-var(theme, 'button-backgroundColor') + border none + &:hover + background-color get-theme-var(theme, 'button--hover-backgroundColor') + &:active + &:active:hover + background-color get-theme-var(theme, 'button--active-backgroundColor') // Danger button(Brand color) @@ -257,12 +239,14 @@ $ui-dark-borderColor = #444444 $ui-dark-backgroundColor = #2C3033 $ui-dark-noteList-backgroundColor = #2C3033 $ui-dark-noteDetail-backgroundColor = #2C3033 +$ui-dark-tagList-backgroundColor = #FFFFFF $ui-dark-tag-backgroundColor = #3A404C $dark-background-color = lighten($ui-dark-backgroundColor, 10%) $ui-dark-text-color = #DDDDDD $ui-dark-button--active-color = #f4f4f4 $ui-dark-button--active-backgroundColor = #3A404C +$ui-dark-button--hover-color = #c0392b $ui-dark-button--hover-backgroundColor = lighten($ui-dark-backgroundColor, 10%) $ui-dark-button--focus-borderColor = lighten(#369DCD, 25%) $ui-dark-topbar-button-color = #939395 @@ -332,19 +316,11 @@ darkTooltip() pointer-events none transition 0.1s -modalDark() - position relative - z-index $modal-z-index - width 100% - background-color $ui-dark-backgroundColor - overflow hidden - border-radius $modal-border-radius - - /******* Solarized Dark theme ********/ $ui-solarized-dark-backgroundColor = #073642 $ui-solarized-dark-noteList-backgroundColor = #073642 $ui-solarized-dark-noteDetail-backgroundColor = #073642 +$ui-solarized-dark-tagList-backgroundColor = #FFFFFF $ui-solarized-dark-text-color = #93a1a1 $ui-solarized-dark-active-color = #2aa198 @@ -356,21 +332,23 @@ $ui-solarized-dark-tag-backgroundColor = #002b36 $ui-solarized-dark-button-backgroundColor = #002b36 $ui-solarized-dark-button--active-color = #93a1a1 $ui-solarized-dark-button--active-backgroundColor = #073642 +$ui-solarized-dark-button--hover-color = #c0392b $ui-solarized-dark-button--hover-backgroundColor = lighten($ui-dark-backgroundColor, 10%) $ui-solarized-dark-button--focus-borderColor = lighten(#369DCD, 25%) -modalSolarizedDark() - position relative - z-index $modal-z-index - width 100% - background-color $ui-solarized-dark-backgroundColor - overflow hidden - border-radius $modal-border-radius +$ui-solarized-dark-kbd-backgroundColor = darken(#21252B, 10%) +$ui-solarized-dark-kbd-color = $ui-solarized-dark-text-color + +$ui-solarized-dark-table-odd-backgroundColor = $ui-solarized-dark-noteDetail-backgroundColor +$ui-solarized-dark-table-even-backgroundColor = darken($ui-solarized-dark-noteDetail-backgroundColor, 10%) +$ui-solarized-dark-table-head-backgroundColor = $ui-solarized-dark-table-even-backgroundColor +$ui-solarized-dark-table-borderColor = lighten(darken(#21252B, 10%), 20%) /******* Monokai theme ********/ $ui-monokai-backgroundColor = #272822 $ui-monokai-noteList-backgroundColor = #272822 $ui-monokai-noteDetail-backgroundColor = #272822 +$ui-monokai-tagList-backgroundColor = #FFFFFF $ui-monokai-text-color = #f8f8f2 $ui-monokai-active-color = #f92672 @@ -382,21 +360,23 @@ $ui-monokai-tag-backgroundColor = #373831 $ui-monokai-button-backgroundColor = #373831 $ui-monokai-button--active-color = white $ui-monokai-button--active-backgroundColor = #f92672 +$ui-monokai-button--hover-color = #f92672 $ui-monokai-button--hover-backgroundColor = lighten($ui-dark-backgroundColor, 10%) $ui-monokai-button--focus-borderColor = lighten(#369DCD, 25%) -modalMonokai() - position relative - z-index $modal-z-index - width 100% - background-color $ui-monokai-backgroundColor - overflow hidden - border-radius $modal-border-radius +$ui-monokai-kbd-backgroundColor = darken(#21252B, 10%) +$ui-monokai-kbd-color = $ui-monokai-text-color + +$ui-monokai-table-odd-backgroundColor = $ui-monokai-noteDetail-backgroundColor +$ui-monokai-table-even-backgroundColor = darken($ui-monokai-noteDetail-backgroundColor, 10%) +$ui-monokai-table-head-backgroundColor = $ui-monokai-table-even-backgroundColor +$ui-monokai-table-borderColor = lighten(darken(#21252B, 10%), 20%) /******* Dracula theme ********/ $ui-dracula-backgroundColor = #282a36 $ui-dracula-noteList-backgroundColor = #282a36 $ui-dracula-noteDetail-backgroundColor = #282a36 +$ui-dracula-tagList-backgroundColor = #f8f8f2 $ui-dracula-text-color = #f8f8f2 $ui-dracula-active-color = #bd93f9 @@ -408,22 +388,79 @@ $ui-dracula-tag-backgroundColor = #8be9fd $ui-dracula-button-backgroundColor = #44475a $ui-dracula-button--active-color = #f8f8f2 $ui-dracula-button--active-backgroundColor = #bd93f9 +$ui-dracula-button--hover-color = #ff79c6 $ui-dracula-button--hover-backgroundColor = lighten($ui-dracula-backgroundColor, 10%) $ui-dracula-button--focus-borderColor = lighten(#44475a, 25%) -colorDraculaDefaultButton() - border-color $ui-dracula-borderColor - color $ui-dracula-text-color - background-color $ui-dracula-button-backgroundColor - &:hover - background-color $ui-dracula-button--hover-backgroundColor - &:active - &:active:hover - background-color $ui-dracula-button--active-backgroundColor -modalDracula() - position relative - z-index $modal-z-index - width 100% - background-color $ui-dracula-backgroundColor - overflow hidden - border-radius $modal-border-radius \ No newline at end of file +$ui-dracula-kbd-backgroundColor = darken(#21252B, 10%) +$ui-dracula-kbd-color = $ui-monokai-text-color + +$ui-dracula-table-odd-backgroundColor = $ui-dracula-noteDetail-backgroundColor +$ui-dracula-table-even-backgroundColor = darken($ui-dracula-noteDetail-backgroundColor, 10%) +$ui-dracula-table-head-backgroundColor = $ui-dracula-table-even-backgroundColor +$ui-dracula-table-borderColor = lighten(darken(#21252B, 10%), 20%) + +/******* Nord theme ********/ +$ui-nord-backgroundColor = #2e3440 +$ui-nord-noteList-backgroundColor = #2e3440 +$ui-nord-noteDetail-backgroundColor = #2e3440 +$ui-nord-tagList-backgroundColor = #FFFFFF + +$ui-nord-text-color = #d8dee9 +$ui-nord-inactive-text-color = #8fbcbb +$ui-nord-active-color = #5e81ac + +$ui-nord-borderColor = #3b4252 + +$ui-nord-tag-backgroundColor = #3b4252 + +$ui-nord-button-backgroundColor = #434c5e +$ui-nord-button--active-color = #d8dee9 +$ui-nord-button--active-backgroundColor = #5e81ac +$ui-nord-button--hover-color = #c0392b +$ui-nord-button--hover-backgroundColor = #434c5e + +$ui-nord-kbd-backgroundColor = $ui-nord-text-color +$ui-nord-kbd-color = $ui-nord-backgroundColor + +$ui-nord-table-odd-backgroundColor = $ui-nord-noteDetail-backgroundColor +$ui-nord-table-even-backgroundColor = darken($ui-nord-noteDetail-backgroundColor, 10%) +$ui-nord-table-head-backgroundColor = $ui-nord-table-even-backgroundColor +$ui-nord-table-borderColor = lighten(darken(#21252B, 10%), 20%) + +/******* Vulcan theme ********/ +$ui-vulcan-backgroundColor = #161719 +$ui-vulcan-noteList-backgroundColor = #161719 +$ui-vulcan-noteDetail-backgroundColor = #161719 +$ui-vulcan-tagList-backgroundColor = #FFFFFF + +$ui-vulcan-text-color = #999999 +$ui-vulcan-inactive-text-color = #999999 +$ui-vulcan-active-color = #ffffff + +$ui-vulcan-borderColor = #282a2e + +$ui-vulcan-tag-backgroundColor = #282a2e + +$ui-vulcan-button-backgroundColor = #282a2e +$ui-vulcan-button--active-color = #a3a8ae +$ui-vulcan-button--active-backgroundColor = #282a2e +$ui-vulcan-button--hover-backgroundColor = #282a2e + +$ui-vulcan-kbd-backgroundColor = lighten($ui-vulcan-text-color, 50%) +$ui-vulcan-kbd-color = $ui-vulcan-backgroundColor + +$ui-vulcan-table-odd-backgroundColor = $ui-vulcan-noteDetail-backgroundColor +$ui-vulcan-table-even-backgroundColor = darken($ui-vulcan-noteDetail-backgroundColor, 10%) +$ui-vulcan-table-head-backgroundColor = $ui-vulcan-table-even-backgroundColor +$ui-vulcan-table-borderColor = lighten(darken(#21252B, 10%), 20%) + + + +debug-theme-var(theme, suffix) + '$ui-' + theme + '-' + suffix + +get-theme-var(theme, suffix) + lookup('$ui-' + theme + '-' + suffix) + +$themes = 'monokai' 'nord' 'vulcan' \ No newline at end of file diff --git a/contributing.md b/contributing.md index fa71d5a5..43d68d85 100644 --- a/contributing.md +++ b/contributing.md @@ -1,3 +1,5 @@ +> [Please consider to contribute to the new Boost Note app too!](https://github.com/BoostIO/BoostNote.next) + # Contributing to Boostnote (English) ### When you open an issue or a bug report @@ -48,14 +50,26 @@ GPL v3 is too strict to be compatible with another license, so we thought it mig # Contributing to Boostnote (Korean) -### 버그 리포트를 보고할 때 -이슈의 양식은 없습니다. 하지만 부탁이 있습니다. - -**개발자 도구를 연 상태의 Boostnote 스크린샷을 첨부해주세요** +### 이슈 또는 버그 리포트를 제출하는 절차 +이슈를 제기할 때에 사용하는 양식(issue template)이 준비되어 있으니, 해당 양식에 맞추어 최대한 구체적인 정보를 첨부하여 주시기 바랍니다. 도움을 주셔서 감사합니다. -### Pull Request의 저작권에 관하여 +### Pull Request를 제출하는 절차 +Pull Request에 사용하는 양식(pull request template)이 준비되어 있으니, 코드를 접수하기 전에 미리 해당 양식을 작성해 주시기 바랍니다. 코드가 해결하고자 하는 문제가 무엇인지 정확히 알면 저희가 훨씬 신속하게 해당 pull request를 검토할 수 있습니다. + +다음 사항을 준수하여 주십시오: +- [`code_style.md`](docs/code_style.md) 에 정리된 코드 스타일 정보를 확인할 것 +- 테스트 코드를 작성하고, 아래와 같은 테스트 커맨드를 실행할 것 +``` +npm run test +``` +- 아래와 같은 린팅 커맨드로 코드를 확인할 것 +``` +npm run lint +``` + +### 저작권에 관한 기준 당신이 pull request를 요청하면, 코드 변경에 대한 저작권을 BoostIO에 양도한다는 것에 동의한다는 의미입니다. @@ -67,12 +81,24 @@ GPL v3 라이센스는 다른 라이센스와 혼합해 사용하기엔 너무 # Contributing to Boostnote (Japanese) ### バグレポートに関してのissueを立てる時 -イシューテンプレートはありませんが、1つお願いがあります。 - -**開発者ツールを開いた状態のBoostnoteのスクリーンショットを貼ってください** +イシューテンプレートがあります。このテンプレートに従って、できるだけ多くの情報を提供してください。 よろしくお願いします。 +### Pull requestを出す時 +Pull requestのテンプレートがあります。このテンプレートを埋めてからコードをサブミットしてください。内容を正確に把握できるPull requestが作られていれば、迅速にレビューを行えます。 + +以下のことを必ず行ってください: +- [`code_style.md`](docs/code_style.md)を読み、コーディングスタイルを確認する +- 変更分のコードに対するテストコードを書き、以下のコマンドでテストを実行する +``` +npm run test +``` +- 以下のコマンドを使って、コードの静的解析を実行する +``` +npm run lint +``` + ### Pull requestの著作権について Pull requestをすることはその変化分のコードの著作権をBoostIOに譲渡することに同意することになります。 @@ -87,17 +113,17 @@ Pull requestをすることはその変化分のコードの著作権をBoostIO # Contributing to Boostnote (Simplified Chinese) ### 当您创建一个issue的时候 -我们对您的issue格式没有要求,但是我们有一个请求: +我们对您的issue格式没有要求,但是我们有一个请求: -**如果可能,请在开发者模式打开的情况下,为我们提供屏幕截图** +**如果可能,请在开发者模式打开的情况下,为我们提供屏幕截图** -(您可以通过`Ctrl+Shift+I`打开开发者模式)。 -感谢您对我们的支持。 +(您可以通过`Ctrl+Shift+I`打开开发者模式)。 +感谢您对我们的支持。 ### 关于您提供的Pull Request的著作权(版权)问题 -如果您提供了一个Pull Request,这表示您将您所修改的代码的著作权移交给BoostIO。 +如果您提供了一个Pull Request,这表示您将您所修改的代码的著作权移交给BoostIO。 -这并不表示Boostnote会成为一个需要付费的软件。如果我们想获得收益,我们会尝试一些其他的方法,比如说云存储、绑定手机软件等。 +这并不表示Boostnote会成为一个需要付费的软件。如果我们想获得收益,我们会尝试一些其他的方法,比如说云存储、绑定手机软件等。 因为GPLv3过于严格,不能和其他的一些协议兼容,所以我们有可能在将来会把BoostNote的协议改为一些较为宽松的协议,比如说BSD、MIT。 --- diff --git a/dev-scripts/dev.js b/dev-scripts/dev.js index 9698a2fe..4cff0ca2 100644 --- a/dev-scripts/dev.js +++ b/dev-scripts/dev.js @@ -15,7 +15,7 @@ const options = { quiet: true } -function startServer () { +function startServer() { config.plugins.push(new webpack.HotModuleReplacementPlugin()) config.entry.main.unshift( `webpack-dev-server/client?http://localhost:${port}/`, @@ -25,7 +25,7 @@ function startServer () { server = new WebpackDevServer(compiler, options) return new Promise((resolve, reject) => { - server.listen(port, 'localhost', function (err) { + server.listen(port, 'localhost', function(err) { if (err) { reject(err) } @@ -48,7 +48,7 @@ function startServer () { }) } -function startElectron () { +function startElectron() { spawn(electron, ['--hot', './index.js'], { stdio: 'inherit' }) .on('close', () => { server.close() diff --git a/docs/build.md b/docs/build.md index 095f8628..937a4a58 100644 --- a/docs/build.md +++ b/docs/build.md @@ -1,6 +1,6 @@ # Build -This page is also available in [Japanese](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [Korean](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [Russain](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [Simplified Chinese](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [French](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md), [Portuguese](https://github.com/BoostIO/Boostnote/blob/master/docs/pt_BR/build.md) and [German](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md). +This page is also available in [Japanese](https://github.com/BoostIO/Boostnote/blob/master/docs/jp/build.md), [Korean](https://github.com/BoostIO/Boostnote/blob/master/docs/ko/build.md), [Russain](https://github.com/BoostIO/Boostnote/blob/master/docs/ru/build.md), [Traditional Chinese](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_TW/build.md), [Simplified Chinese](https://github.com/BoostIO/Boostnote/blob/master/docs/zh_CN/build.md), [French](https://github.com/BoostIO/Boostnote/blob/master/docs/fr/build.md), [Portuguese](https://github.com/BoostIO/Boostnote/blob/master/docs/pt_BR/build.md) and [German](https://github.com/BoostIO/Boostnote/blob/master/docs/de/build.md). ## Environments @@ -37,7 +37,7 @@ Visit the page for the pull request and look at the end of the url for the PR nu https://github.com/BoostIO/Boostnote/pull/2794 In the following, replace \ with that number (no brackets). -For the above url, you would replace \ with 2794 +For URLs below, you would replace \ with 2794 _If you do not have a local copy of the master branch yet_ ``` @@ -82,13 +82,17 @@ Distribution packages are created by exec `grunt build` on Linux platform (e.g. After installing the supported version of `node` and `npm`, install build dependency packages. -Ubuntu/Debian: +``` +$ yarn add --dev grunt-electron-installer-debian grunt-electron-installer-redhat +``` + +**Ubuntu/Debian:** ``` $ sudo apt-get install -y rpm fakeroot ``` -Fedora: +**Fedora:** ``` $ sudo dnf install -y dpkg dpkg-dev rpm-build fakeroot @@ -100,4 +104,4 @@ Then execute `grunt build`. $ grunt build ``` -You will find `.deb` and `.rpm` in the `dist` directory. +> You will find `.deb` and `.rpm` in the `dist` directory. diff --git a/docs/ko/build.md b/docs/ko/build.md index 17e9c712..d9a5f4a9 100644 --- a/docs/ko/build.md +++ b/docs/ko/build.md @@ -5,8 +5,6 @@ * npm: 6.x * node: 8.x -`$ grunt pre-build`를 `npm v5.x`에서 실행할 수 없기 때문에, 반드시 `npm v4.x`를 사용하셔야 합니다. - ## 개발 개발에 있어서 Webpack HRM을 사용합니다. @@ -29,6 +27,34 @@ $ yarn run dev > 1. 콤포넌트의 컨스트럭터 함수를 수정할 경우 > 2. 새로운 CSS코드를 추가할 경우(1.과 같은 이유: CSS클래스는 콤포넌트마다 다시 만들어 지는데, 이 작업은 컨스트럭터에서 일어납니다.) +## Pull Request에 사용된 코드를 적용하는 방법 +관련된 Pull request 페이지를 방문하여, url 스트링 마지막에 표기된 PR 번호를 확인합니다. +
+https://github.com/BoostIO/Boostnote/pull/2794
+
+아래의 커맨드를 실행하면서, \ 대신에 위에서 확인한 번호를 입력합니다 (부등호 신호는 빼고 입력하세요). +위에 보여진 예시의 경우, \ 자리에 2794를 입력하면 됩니다. + +_본인의 로컬 컴퓨터에 마스터 브랜치가 복사되어 있지 않은 경우_ +``` +git clone https://github.com/BoostIO/Boostnote.git +cd Boostnote +git fetch origin pull//head: +git checkout +``` + +_이미 마스터 브랜치를 로컬 컴퓨터에 저장해둔 경우_ +``` +git fetch origin pull//head: +git checkout +``` + +_To compile and run the code_ +``` +yarn +yarn run dev +``` + ## 배포 Boostnote에서는 배포 자동화를 위하여 그런트를 사용합니다. @@ -43,3 +69,31 @@ grunt pre-build 실행 파일은 `dist`에서 찾을 수 있습니다. 이 경우, 인증이 되어있지 않기 때문에 업데이터는 사용할 수 없습니다. 필요로 하다면, 이 실행파일에 Codesign나 Authenticode등의 서명을 할 수 있습니다. + +## 독자적인 배포판을 제작하는 방법 (deb, rpm) + +배포판 패키지를 제작하려면 (우분투, 페도라 등) 리눅스 플랫폼에서 `grunt build` 커맨드를 실행하면 됩니다. + +> 참조: 동일한 환경에서 `.deb` 파일과 `.rpm` 파일을 모두 만들 수 있습니다. + +지원되는 버전의 `node`와 `npm`을 설치한 다음, 빌드에 필요한 패키지를 설치합니다. + +우분투/데비안 환경 (Ubuntu/Debian): + +``` +$ sudo apt-get install -y rpm fakeroot +``` + +페도라 환경 (Fedora): + +``` +$ sudo dnf install -y dpkg dpkg-dev rpm-build fakeroot +``` + +그 다음 `grunt build` 커맨드를 실행합니다. + +``` +$ grunt build +``` + +`dist` 디렉토리에 `.deb` 파일과 `.rpm` 파일이 새롭게 생성됩니다. diff --git a/docs/ko/debug.md b/docs/ko/debug.md index 290eee9c..6d68d951 100644 --- a/docs/ko/debug.md +++ b/docs/ko/debug.md @@ -1,5 +1,7 @@ # Boostnote의 디버그 방법(Electron app) +## 구글 크롬 Developer Tools를 사용한 디버깅 + Boostnote는 Electron 애플리케이션이므로 Chromium위에서 작동합니다. 그렇기 때문에 개발자분들은 Google Chrome 브라우저에서 처럼 `Developer Tools`를 사용하실 수 있습니다. 다음과 같이 `Developer Tools`를 실행할 수 있습니다: @@ -10,12 +12,26 @@ Boostnote는 Electron 애플리케이션이므로 Chromium위에서 작동합니 에러가 발생할 때에는, 에러메시지가 `console`위에 표시 됩니다. -## 디버깅 +### 디버깅 예를들면 `debugger`를 사용하여 코드 안에서 다음과 같이 일시 정지지점을 설정할 수 있습니다: ![debugger](https://cloud.githubusercontent.com/assets/11307908/24343879/9459efea-127d-11e7-9943-f60bf7f66d4a.png) 이는 단순한 하나의 예시에 불과합니다. 자기자신에게 가장 잘 맞는 디버그 방법을 찾는 것도 좋을 것 입니다. -## 참고 + ### 참고 * [디버그에 관한 Google Chrome의 공식 문서](https://developer.chrome.com/devtools) + +## 비주얼 스튜디오 코드를 사용한 디버깅 + +1. **[크롬 디버깅 플러그인](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome 'Install Debugger for Chrome')** 을 비주얼 스튜디오 코드에 설치한 후, 프로그램을 닫았다가 재실행합니다. +2. **Shift+Command+B** 키를 누르거나, **Terminal** 메뉴 하단에 있는 **Run Build Task** 메뉴를 선택한 후 **Build Boostnote** 를 선택합니다. 아니면 터미널에서 곧바로 `yarn run watch`를 실행해도 됩니다. +3. 위의 절차가 실행되고 있을 때, 사이드바 **Activity Bar**에서 **Debug view**를 선택합니다. 키보드 단축키로는 **Shift+Command+D**를 눌러도 됩니다.. +4. **Debug configuration**에서 **Boostnote All** 설정을 선택한 후, 초록색 화살표를 클릭하거나 **F5** 키를 누르면 디버깅이 시작됩니다. +5. 이 시점에서는 **Boostnote**가 실행되고 있을 텐데, 두 개의 프로세스가 진행중인 것을 볼 수 있을 겁니다. 바로 **Boostnote Main** 프로세스와 **Boostnote Renderer** 프로세스입니다. 이제 비주얼 스튜디오 코드에서 곧바로 **디버깅 정지지점 (debug breakpoint)** 을 설정할 수 있습니다. 만약에 지정한 **정지지점 (breakpoint)** 이 등록되지 않는다면, **Boostnote Renderer** 와 **Boostnote Main** 프로세스 사이를 번갈아 확인해 보아야 합니다. + + +### 참고 + +- [일렉트론 애플리케이션 디버깅 공식 튜토리얼](https://electronjs.org/docs/tutorial/application-debugging) +- [비쥬얼 스튜디오 코드용 크롬 디버깅 플러그인](https://marketplace.visualstudio.com/items?itemName=msjsdiag.debugger-for-chrome) diff --git a/docs/zh_TW/build.md b/docs/zh_TW/build.md index 151f946d..360440e5 100644 --- a/docs/zh_TW/build.md +++ b/docs/zh_TW/build.md @@ -10,7 +10,6 @@ ## 開發 我們使用 Webpack HMR 來開發 Boostnote。 - 在專案根目錄底下執行下列指令,將會以原始設置啟動 Boostnote。 **用 yarn 來安裝必要 packages** @@ -19,41 +18,74 @@ $ yarn ``` -**開始開發** +**編譯及執行** ``` $ yarn run dev ``` -> ### Notice +> ### 注意 > -> There are some cases where you have to refresh the app manually. +> 以下是一些可能要手動重新啟動程式的情況。 > -> 1. When editing a constructor method of a component -> 2. When adding a new css class (similar to 1: the CSS class is re-written by each component. This process occurs at the Constructor method.) +> 1. 修改一個 component 的 constructor 方法時。 +> 2. 新增新的 CSS 類別時 (和 1 很類似:CSS 類別會被每個 component 重寫過。這個過程在 constructor 方法中發生)。 -## Deploy +## 使用 Pull Requests 中的程式碼 +瀏覽 pull request 的頁面,從 URL 的後面找到 PR 號碼。 -We use Grunt to automate deployment. -You can build the program by using `grunt`. However, we don't recommend this because the default task includes codesign and authenticode. +
+https://github.com/BoostIO/Boostnote/pull/2794
+
+接著,於底下步驟中把 \ 換成這個號碼 (沒有括號)。 +請將下方 URL 中的 \ 換置成 2794。 -So, we've prepared a separate script which just makes an executable file. +_如果您還未取得一份 master branch 的本地備份_ +``` +git clone https://github.com/BoostIO/Boostnote.git +cd Boostnote +git fetch origin pull//head: +git checkout +``` + +_如果您已經擁有了 master branch_ +``` +git fetch origin pull//head: +git checkout +``` + +_編譯及執行程式碼_ +``` +yarn +yarn run dev +``` + +## 佈署 + +我們用 Grunt 做自動佈署。 +您能使用 `grung` 建構本程式。然而,我們並不建議這麼做,因為預設工作流程包含了程式簽名以及 Authenticode 驗證。 + +所以,我們準備了一份額外的腳本用於建構可執行檔。 ``` grunt pre-build ``` -You will find the executable in the `dist` directory. Note, the auto updater won't work because the app isn't signed. +您可以在 `dist` 資料夾下找到可執行檔。注意,自動更新功能 (auto updater) 並不會生效,因為程式沒有被簽署過。 -If you find it necessary, you can use codesign or authenticode with this executable. +必要時您可以使用程式簽名或 authenticode 驗證執行檔。 -## Make own distribution packages (deb, rpm) +## 建立您自己的發行版套件 (deb, rpm) -Distribution packages are created by exec `grunt build` on Linux platform (e.g. Ubuntu, Fedora). +發行版套件可以透過在 Linux 平台上 (如 Ubuntu, Fedora) 執行 `grunt build` 來建立。 -> Note: You can create both `.deb` and `.rpm` in a single environment. +> 注意:您可以在同個環境中同時建立 `.deb` 及`.rpm` 。 -After installing the supported version of `node` and `npm`, install build dependency packages. +安裝支援版本的 `node` 和 `npm` 後,安裝編譯相依套件。 + +``` +$ yarn add --dev grunt-electron-installer-debian grunt-electron-installer-redhat +``` Ubuntu/Debian: @@ -67,10 +99,10 @@ Fedora: $ sudo dnf install -y dpkg dpkg-dev rpm-build fakeroot ``` -Then execute `grunt build`. +接著執行 `grunt build`。 ``` $ grunt build ``` -You will find `.deb` and `.rpm` in the `dist` directory. +> 於 `dist` 資料夾下找到 `.deb` 及 `.rpm`。 diff --git a/gruntfile.js b/gruntfile.js index 207f8685..f235ceaa 100644 --- a/gruntfile.js +++ b/gruntfile.js @@ -5,13 +5,15 @@ const packager = require('electron-packager') const WIN = process.platform === 'win32' -module.exports = function (grunt) { +module.exports = function(grunt) { var authCode try { authCode = grunt.file.readJSON('secret/auth_code.json') } catch (e) { if (e.origError.code === 'ENOENT') { - console.warn('secret/auth_code.json is not found. CodeSigning is not available.') + console.warn( + 'secret/auth_code.json is not found. CodeSigning is not available.' + ) } } const OSX_COMMON_NAME = authCode != null ? authCode.OSX_COMMON_NAME : '' @@ -41,10 +43,7 @@ module.exports = function (grunt) { genericName: 'Boostnote', productDescription: 'The opensource note app for developers.', arch: 'amd64', - categories: [ - 'Development', - 'Utility' - ], + categories: ['Development', 'Utility'], icon: path.join(__dirname, 'resources/app.png'), bin: 'Boostnote' }, @@ -60,10 +59,7 @@ module.exports = function (grunt) { genericName: 'Boostnote', productDescription: 'The opensource note app for developers.', arch: 'x86_64', - categories: [ - 'Development', - 'Utility' - ], + categories: ['Development', 'Utility'], icon: path.join(__dirname, 'resources/app.png'), bin: 'Boostnote' }, @@ -80,18 +76,21 @@ module.exports = function (grunt) { grunt.loadNpmTasks('grunt-electron-installer-redhat') } - grunt.registerTask('compile', function () { + grunt.registerTask('compile', function() { var done = this.async() - var execPath = path.join('node_modules', '.bin', 'webpack') + ' --config webpack-production.config.js' + var execPath = + path.join('node_modules', '.bin', 'webpack') + + ' --config webpack-production.config.js' grunt.log.writeln(execPath) - ChildProcess.exec(execPath, + ChildProcess.exec( + execPath, { env: Object.assign({}, process.env, { BABEL_ENV: 'production', NODE_ENV: 'production' }) }, - function (err, stdout, stderr) { + function(err, stdout, stderr) { grunt.log.writeln(stdout) if (err) { @@ -105,7 +104,7 @@ module.exports = function (grunt) { ) }) - grunt.registerTask('pack', function (platform) { + grunt.registerTask('pack', function(platform) { grunt.log.writeln(path.join(__dirname, 'dist')) var done = this.async() var opts = { @@ -137,7 +136,7 @@ module.exports = function (grunt) { InternalName: 'Boostnote' } }) - packager(opts, function (err, appPath) { + packager(opts, function(err, appPath) { if (err) { grunt.log.writeln(err) done(err) @@ -153,7 +152,7 @@ module.exports = function (grunt) { icon: path.join(__dirname, 'resources/app.icns'), 'app-category-type': 'public.app-category.developer-tools' }) - packager(opts, function (err, appPath) { + packager(opts, function(err, appPath) { if (err) { grunt.log.writeln(err) done(err) @@ -168,7 +167,7 @@ module.exports = function (grunt) { icon: path.join(__dirname, 'resources/app.icns'), 'app-category-type': 'public.app-category.developer-tools' }) - packager(opts, function (err, appPath) { + packager(opts, function(err, appPath) { if (err) { grunt.log.writeln(err) done(err) @@ -180,15 +179,16 @@ module.exports = function (grunt) { } }) - grunt.registerTask('codesign', function (platform) { + grunt.registerTask('codesign', function(platform) { var done = this.async() if (process.platform !== 'darwin') { done(false) return } - ChildProcess.exec(`codesign --verbose --deep --force --sign \"${OSX_COMMON_NAME}\" dist/Boostnote-darwin-x64/Boostnote.app`, - function (err, stdout, stderr) { + ChildProcess.exec( + `codesign --verbose --deep --force --sign \"${OSX_COMMON_NAME}\" dist/Boostnote-darwin-x64/Boostnote.app`, + function(err, stdout, stderr) { grunt.log.writeln(stdout) if (err) { grunt.log.writeln(err) @@ -197,44 +197,43 @@ module.exports = function (grunt) { return } done() - }) + } + ) }) - grunt.registerTask('create-osx-installer', function () { + grunt.registerTask('create-osx-installer', function() { var done = this.async() var execPath = 'appdmg appdmg.json dist/Boostnote-mac.dmg' grunt.log.writeln(execPath) - ChildProcess.exec(execPath, - function (err, stdout, stderr) { - grunt.log.writeln(stdout) - if (err) { - grunt.log.writeln(err) - grunt.log.writeln(stderr) - done(false) - return - } - done() - }) + ChildProcess.exec(execPath, function(err, stdout, stderr) { + grunt.log.writeln(stdout) + if (err) { + grunt.log.writeln(err) + grunt.log.writeln(stderr) + done(false) + return + } + done() + }) }) - grunt.registerTask('zip', function (platform) { + grunt.registerTask('zip', function(platform) { var done = this.async() switch (platform) { case 'osx': - var execPath = 'cd dist/Boostnote-darwin-x64 && zip -r -y -q ../Boostnote-mac.zip Boostnote.app' + var execPath = + 'cd dist/Boostnote-darwin-x64 && zip -r -y -q ../Boostnote-mac.zip Boostnote.app' grunt.log.writeln(execPath) - ChildProcess.exec(execPath, - function (err, stdout, stderr) { - grunt.log.writeln(stdout) - if (err) { - grunt.log.writeln(err) - grunt.log.writeln(stderr) - done(false) - return - } - done() + ChildProcess.exec(execPath, function(err, stdout, stderr) { + grunt.log.writeln(stdout) + if (err) { + grunt.log.writeln(err) + grunt.log.writeln(stderr) + done(false) + return } - ) + done() + }) break default: done() @@ -242,7 +241,7 @@ module.exports = function (grunt) { } }) - function getTarget () { + function getTarget() { switch (process.platform) { case 'darwin': return 'osx' @@ -255,7 +254,7 @@ module.exports = function (grunt) { } } - grunt.registerTask('build', function (platform) { + grunt.registerTask('build', function(platform) { if (platform == null) platform = getTarget() switch (platform) { @@ -263,15 +262,26 @@ module.exports = function (grunt) { grunt.task.run(['compile', 'pack:win', 'create-windows-installer']) break case 'osx': - grunt.task.run(['compile', 'pack:osx', 'codesign', 'create-osx-installer', 'zip:osx']) + grunt.task.run([ + 'compile', + 'pack:osx', + 'codesign', + 'create-osx-installer', + 'zip:osx' + ]) break case 'linux': - grunt.task.run(['compile', 'pack:linux', 'electron-installer-debian', 'electron-installer-redhat']) + grunt.task.run([ + 'compile', + 'pack:linux', + 'electron-installer-debian', + 'electron-installer-redhat' + ]) break } }) - grunt.registerTask('pre-build', function (platform) { + grunt.registerTask('pre-build', function(platform) { if (platform == null) platform = getTarget() switch (platform) { @@ -286,11 +296,11 @@ module.exports = function (grunt) { } }) - grunt.registerTask('bfm', function () { + grunt.registerTask('bfm', function() { const Color = require('color') const parseCSS = require('css').parse - function generateRule (selector, bgColor, fgColor) { + function generateRule(selector, bgColor, fgColor) { if (bgColor.isLight()) { bgColor = bgColor.mix(fgColor, 0.05) } else { @@ -298,48 +308,79 @@ module.exports = function (grunt) { } if (selector && selector.length > 0) { - return `${selector} .cm-table-row-even { background-color: ${bgColor.rgb().string()}; }` + return `${selector} .cm-table-row-even { background-color: ${bgColor + .rgb() + .string()}; }` } else { - return `.cm-table-row-even { background-color: ${bgColor.rgb().string()}; }` + return `.cm-table-row-even { background-color: ${bgColor + .rgb() + .string()}; }` } } const root = path.join(__dirname, 'node_modules/codemirror/theme/') - const colors = fs.readdirSync(root).filter(file => file !== 'solarized.css').map(file => { - const css = parseCSS(fs.readFileSync(path.join(root, file), 'utf8')) + const colors = fs + .readdirSync(root) + .filter(file => file !== 'solarized.css') + .map(file => { + const css = parseCSS(fs.readFileSync(path.join(root, file), 'utf8')) - const rules = css.stylesheet.rules.filter(rule => rule.selectors && /\b\.CodeMirror$/.test(rule.selectors[0])) - if (rules.length === 1) { - let bgColor = Color('white') - let fgColor = Color('black') + const rules = css.stylesheet.rules.filter( + rule => rule.selectors && /\b\.CodeMirror$/.test(rule.selectors[0]) + ) + if (rules.length === 1) { + let bgColor = Color('white') + let fgColor = Color('black') - rules[0].declarations.forEach(declaration => { - if (declaration.property === 'background-color' || declaration.property === 'background') { - bgColor = Color(declaration.value.split(' ')[0]) - } else if (declaration.property === 'color') { - const value = /^(.*?)(?:\s*!important)?$/.exec(declaration.value)[1] - const match = /^rgba\((.*?),\s*1\)$/.exec(value) - if (match) { - fgColor = Color(`rgb(${match[1]})`) - } else { - fgColor = Color(value) + rules[0].declarations.forEach(declaration => { + if ( + declaration.property === 'background-color' || + declaration.property === 'background' + ) { + bgColor = Color(declaration.value.split(' ')[0]) + } else if (declaration.property === 'color') { + const value = /^(.*?)(?:\s*!important)?$/.exec( + declaration.value + )[1] + const match = /^rgba\((.*?),\s*1\)$/.exec(value) + if (match) { + fgColor = Color(`rgb(${match[1]})`) + } else { + fgColor = Color(value) + } } - } - }) + }) - return generateRule(rules[0].selectors[0], bgColor, fgColor) - } - }).filter(value => !!value) + return generateRule(rules[0].selectors[0], bgColor, fgColor) + } + }) + .filter(value => !!value) // default colors.unshift(generateRule(null, Color('white'), Color('black'))) // solarized dark - colors.push(generateRule('.cm-s-solarized.cm-s-dark', Color('#002b36'), Color('#839496'))) + colors.push( + generateRule( + '.cm-s-solarized.cm-s-dark', + Color('#002b36'), + Color('#839496') + ) + ) // solarized light - colors.push(generateRule('.cm-s-solarized.cm-s-light', Color('#fdf6e3'), Color('#657b83'))) + colors.push( + generateRule( + '.cm-s-solarized.cm-s-light', + Color('#fdf6e3'), + Color('#657b83') + ) + ) - fs.writeFileSync(path.join(__dirname, 'extra_scripts/codemirror/mode/bfm/bfm.css'), colors.join('\n'), 'utf8') + fs.writeFileSync( + path.join(__dirname, 'extra_scripts/codemirror/mode/bfm/bfm.css'), + colors.join('\n'), + 'utf8' + ) }) grunt.registerTask('default', ['build']) diff --git a/index.js b/index.js index 96f98e73..bcc4e879 100644 --- a/index.js +++ b/index.js @@ -4,30 +4,30 @@ const path = require('path') var error = null -function execMainApp () { +function execMainApp() { const appRootPath = path.join(process.execPath, '../..') const updateDotExePath = path.join(appRootPath, 'Update.exe') const exeName = path.basename(process.execPath) - function spawnUpdate (args, cb) { + function spawnUpdate(args, cb) { var stdout = '' var updateProcess = null try { updateProcess = ChildProcess.spawn(updateDotExePath, args) } catch (e) { - process.nextTick(function () { + process.nextTick(function() { cb(e) }) } - updateProcess.stdout.on('data', function (data) { + updateProcess.stdout.on('data', function(data) { stdout += data }) - updateProcess.on('error', function (_error) { + updateProcess.on('error', function(_error) { error = _error }) - updateProcess.on('close', function (code, signal) { + updateProcess.on('close', function(code, signal) { if (code !== 0) { error = new Error('Command failed: #{signal ? code}') error.code = code @@ -38,7 +38,7 @@ function execMainApp () { }) } - var handleStartupEvent = function () { + var handleStartupEvent = function() { if (process.platform !== 'win32') { return false } @@ -46,7 +46,7 @@ function execMainApp () { var squirrelCommand = process.argv[1] switch (squirrelCommand) { case '--squirrel-install': - spawnUpdate(['--createShortcut', exeName], function (err) { + spawnUpdate(['--createShortcut', exeName], function(err) { if (err) console.error(err) app.quit() }) @@ -55,7 +55,7 @@ function execMainApp () { app.quit() return true case '--squirrel-uninstall': - spawnUpdate(['--removeShortcut', exeName], function (err) { + spawnUpdate(['--removeShortcut', exeName], function(err) { if (err) console.error(err) app.quit() }) diff --git a/lib/ipcServer.js b/lib/ipcServer.js index 42e229d3..41d3ea7b 100644 --- a/lib/ipcServer.js +++ b/lib/ipcServer.js @@ -7,7 +7,7 @@ nodeIpc.config.id = 'node' nodeIpc.config.retry = 1500 nodeIpc.config.silent = true -function toggleMainWindow () { +function toggleMainWindow() { switch (global.process.platform) { case 'darwin': if (mainWindow.isFocused()) { @@ -52,14 +52,14 @@ ipcMain.on('config-renew', (e, payload) => { nodeIpc.serve( path.join(app.getPath('userData'), 'boostnote.service'), - function () { - nodeIpc.server.on('connect', function (socket) { + function() { + nodeIpc.server.on('connect', function(socket) { nodeIpc.log('ipc server >> socket joinned'.rainbow) - socket.on('close', function () { + socket.on('close', function() { nodeIpc.log('ipc server >> socket closed'.rainbow) }) }) - nodeIpc.server.on('error', function (err) { + nodeIpc.server.on('error', function(err) { nodeIpc.log('Node IPC error'.rainbow, err) }) } diff --git a/lib/main-app.js b/lib/main-app.js index f8ee1ecf..2293fd58 100644 --- a/lib/main-app.js +++ b/lib/main-app.js @@ -36,8 +36,9 @@ const updater = new GhReleases(ghReleasesOpts) // Check for updates // `status` returns true if there is a new update available -function checkUpdate () { - if (!isPackaged) { // Prevents app from attempting to update when in dev mode. +function checkUpdate() { + if (!isPackaged) { + // Prevents app from attempting to update when in dev mode. console.log('Updates are disabled in Development mode, see main-app.js') return true } @@ -58,29 +59,29 @@ function checkUpdate () { }) } -updater.on('update-downloaded', (info) => { +updater.on('update-downloaded', info => { if (mainWindow != null) { mainWindow.webContents.send('update-ready', 'Update available!') isUpdateReady = true } }) -updater.autoUpdater.on('error', (err) => { +updater.autoUpdater.on('error', err => { console.error(err) }) -ipc.on('update-app-confirm', function (event, msg) { +ipc.on('update-app-confirm', function(event, msg) { if (isUpdateReady) { mainWindow.removeAllListeners() updater.install() } }) -app.on('window-all-closed', function () { +app.on('window-all-closed', function() { app.quit() }) -app.on('ready', function () { +app.on('ready', function() { mainWindow = require('./main-window') var template = require('./main-menu') @@ -100,7 +101,7 @@ app.on('ready', function () { } // Check update every day - setInterval(function () { + setInterval(function() { if (isPackaged) checkUpdate() }, 1000 * 60 * 60 * 24) @@ -108,7 +109,7 @@ app.on('ready', function () { setTimeout(() => { if (isPackaged) checkUpdate() - ipc.on('update-check', function (event, msg) { + ipc.on('update-check', function(event, msg) { if (isUpdateReady) { mainWindow.webContents.send('update-ready', 'Update available!') } else { diff --git a/lib/main-menu.js b/lib/main-menu.js index 124c6675..7caef0bf 100644 --- a/lib/main-menu.js +++ b/lib/main-menu.js @@ -11,68 +11,68 @@ const LINUX = process.platform === 'linux' const boost = macOS ? { - label: 'Boostnote', - submenu: [ - { - label: 'About Boostnote', - selector: 'orderFrontStandardAboutPanel:' - }, - { - type: 'separator' - }, - { - label: 'Preferences', - accelerator: 'Command+,', - click () { - mainWindow.webContents.send('side:preferences') + label: 'Boostnote', + submenu: [ + { + label: 'About Boostnote', + selector: 'orderFrontStandardAboutPanel:' + }, + { + type: 'separator' + }, + { + label: 'Preferences', + accelerator: 'Command+,', + click() { + mainWindow.webContents.send('side:preferences') + } + }, + { + type: 'separator' + }, + { + label: 'Hide Boostnote', + accelerator: 'Command+H', + selector: 'hide:' + }, + { + label: 'Hide Others', + accelerator: 'Command+Shift+H', + selector: 'hideOtherApplications:' + }, + { + label: 'Show All', + selector: 'unhideAllApplications:' + }, + { + type: 'separator' + }, + { + label: 'Quit Boostnote', + role: 'quit', + accelerator: 'CommandOrControl+Q' } - }, - { - type: 'separator' - }, - { - label: 'Hide Boostnote', - accelerator: 'Command+H', - selector: 'hide:' - }, - { - label: 'Hide Others', - accelerator: 'Command+Shift+H', - selector: 'hideOtherApplications:' - }, - { - label: 'Show All', - selector: 'unhideAllApplications:' - }, - { - type: 'separator' - }, - { - label: 'Quit Boostnote', - role: 'quit', - accelerator: 'CommandOrControl+Q' - } - ] - } + ] + } : { - label: 'Boostnote', - submenu: [ - { - label: 'Preferences', - accelerator: 'Control+,', - click () { - mainWindow.webContents.send('side:preferences') + label: 'Boostnote', + submenu: [ + { + label: 'Preferences', + accelerator: 'Control+,', + click() { + mainWindow.webContents.send('side:preferences') + } + }, + { + type: 'separator' + }, + { + role: 'quit', + accelerator: 'Control+Q' } - }, - { - type: 'separator' - }, - { - role: 'quit', - accelerator: 'Control+Q' - } - ] - } + ] + } const file = { label: 'File', @@ -80,28 +80,28 @@ const file = { { label: 'New Note', accelerator: 'CommandOrControl+N', - click () { + click() { mainWindow.webContents.send('top:new-note') } }, { label: 'Focus Note', - accelerator: macOS ? 'Command+E' : 'Control+E', - click () { + accelerator: 'CommandOrControl+E', + click() { mainWindow.webContents.send('detail:focus') } }, { label: 'Delete Note', - accelerator: macOS ? 'Command+Shift+Backspace' : 'Control+Shift+Backspace', - click () { + accelerator: 'CommandOrControl+Shift+Backspace', + click() { mainWindow.webContents.send('detail:delete') } }, { label: 'Clone Note', - accelerator: macOS ? 'Command+D' : 'Control+D', - click () { + accelerator: 'CommandOrControl+D', + click() { mainWindow.webContents.send('list:clone') } }, @@ -113,7 +113,7 @@ const file = { submenu: [ { label: 'Plain Text, MarkDown (.txt, .md)', - click () { + click() { mainWindow.webContents.send('import:file') } } @@ -124,28 +124,28 @@ const file = { submenu: [ { label: 'Plain Text (.txt)', - click () { + click() { mainWindow.webContents.send('list:isMarkdownNote', 'export-txt') mainWindow.webContents.send('export:save-text') } }, { label: 'MarkDown (.md)', - click () { + click() { mainWindow.webContents.send('list:isMarkdownNote', 'export-md') mainWindow.webContents.send('export:save-md') } }, { label: 'HTML (.html)', - click () { + click() { mainWindow.webContents.send('list:isMarkdownNote', 'export-html') mainWindow.webContents.send('export:save-html') } }, { label: 'PDF (.pdf)', - click () { + click() { mainWindow.webContents.send('list:isMarkdownNote', 'export-pdf') mainWindow.webContents.send('export:save-pdf') } @@ -158,13 +158,13 @@ const file = { { label: 'Generate/Update Markdown TOC', accelerator: 'Shift+Ctrl+T', - click () { + click() { mainWindow.webContents.send('code:generate-toc') } }, { label: 'Format Table', - click () { + click() { mainWindow.webContents.send('code:format-table') } }, @@ -174,7 +174,7 @@ const file = { { label: 'Print', accelerator: 'CommandOrControl+P', - click () { + click() { mainWindow.webContents.send('list:isMarkdownNote', 'print') mainWindow.webContents.send('print') } @@ -183,20 +183,25 @@ const file = { } if (LINUX) { - file.submenu.push({ - type: 'separator' - }, { - label: 'Preferences', - accelerator: 'Control+,', - click () { - mainWindow.webContents.send('side:preferences') + file.submenu.push( + { + type: 'separator' + }, + { + label: 'Preferences', + accelerator: 'Control+,', + click() { + mainWindow.webContents.send('side:preferences') + } + }, + { + type: 'separator' + }, + { + role: 'quit', + accelerator: 'Control+Q' } - }, { - type: 'separator' - }, { - role: 'quit', - accelerator: 'Control+Q' - }) + ) } const edit = { @@ -241,7 +246,7 @@ const edit = { { label: 'Add Tag', accelerator: 'CommandOrControl+Shift+T', - click () { + click() { mainWindow.webContents.send('editor:add-tag') } } @@ -254,14 +259,14 @@ const view = { { label: 'Reload', accelerator: 'CommandOrControl+R', - click () { + click() { BrowserWindow.getFocusedWindow().reload() } }, { label: 'Toggle Developer Tools', - accelerator: macOS ? 'Command+Alt+I' : 'Control+Shift+I', - click () { + accelerator: 'CommandOrControl+Alt+I', + click() { BrowserWindow.getFocusedWindow().toggleDevTools() } }, @@ -271,14 +276,14 @@ const view = { { label: 'Next Note', accelerator: 'CommandOrControl+]', - click () { + click() { mainWindow.webContents.send('list:next') } }, { label: 'Previous Note', accelerator: 'CommandOrControl+[', - click () { + click() { mainWindow.webContents.send('list:prior') } }, @@ -288,7 +293,7 @@ const view = { { label: 'Focus Search', accelerator: 'CommandOrControl+Shift+L', - click () { + click() { mainWindow.webContents.send('top:focus-search') } }, @@ -298,14 +303,14 @@ const view = { { label: 'Toggle Full Screen', accelerator: macOS ? 'Command+Control+F' : 'F11', - click () { + click() { mainWindow.setFullScreen(!mainWindow.isFullScreen()) } }, { label: 'Toggle Side Bar', accelerator: 'CommandOrControl+B', - click () { + click() { mainWindow.webContents.send('editor:fullscreen') } }, @@ -314,22 +319,22 @@ const view = { }, { label: 'Actual Size', - accelerator: macOS ? 'CommandOrControl+0' : 'Control+0', - click () { + accelerator: 'CommandOrControl+0', + click() { mainWindow.webContents.send('status:zoomreset') } }, { label: 'Zoom In', - accelerator: macOS ? 'CommandOrControl+=' : 'Control+=', - click () { + accelerator: 'CommandOrControl+=', + click() { mainWindow.webContents.send('status:zoomin') } }, { label: 'Zoom Out', - accelerator: macOS ? 'CommandOrControl+-' : 'Control+-', - click () { + accelerator: 'CommandOrControl+-', + click() { mainWindow.webContents.send('status:zoomout') } } @@ -382,34 +387,58 @@ const help = { submenu: [ { label: 'Boostnote official site', - click () { shell.openExternal('https://boostnote.io/') } + click() { + shell.openExternal('https://boostnote.io/') + } + }, + { + label: 'Wiki', + click() { + shell.openExternal('https://github.com/BoostIO/Boostnote/wiki') + } }, { label: 'Issue Tracker', - click () { shell.openExternal('https://github.com/BoostIO/Boostnote/issues') } + click() { + shell.openExternal('https://github.com/BoostIO/Boostnote/issues') + } }, { label: 'Changelog', - click () { shell.openExternal('https://github.com/BoostIO/boost-releases') } + click() { + shell.openExternal('https://github.com/BoostIO/boost-releases') + } }, { label: 'Cheatsheets', submenu: [ { label: 'Markdown', - click () { shell.openExternal('https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet') } + click() { + shell.openExternal( + 'https://github.com/adam-p/markdown-here/wiki/Markdown-Cheatsheet' + ) + } }, { label: 'Latex', - click () { shell.openExternal('https://katex.org/docs/supported.html') } + click() { + shell.openExternal('https://katex.org/docs/supported.html') + } }, { label: 'HTML', - click () { shell.openExternal('https://htmlcheatsheet.com/') } + click() { + shell.openExternal('https://htmlcheatsheet.com/') + } }, { label: 'Boostnote', - click () { shell.openExternal('https://github.com/TobseF/boostnote-markdown-cheatsheet/blob/master/BOOSTNOTE_MARKDOWN_CHEAT_SHEET.md') } + click() { + shell.openExternal( + 'https://github.com/TobseF/boostnote-markdown-cheatsheet/blob/master/BOOSTNOTE_MARKDOWN_CHEAT_SHEET.md' + ) + } } ] }, @@ -418,7 +447,7 @@ const help = { }, { label: 'About', - click () { + click() { const version = electron.app.getVersion() const electronVersion = process.versions.electron const chromeVersion = process.versions.chrome @@ -426,20 +455,20 @@ const help = { const v8Version = process.versions.v8 const OSInfo = `${os.type()} ${os.arch()} ${os.release()}` const detail = `Version: ${version}\nElectron: ${electronVersion}\nChrome: ${chromeVersion}\nNode.js: ${nodeVersion}\nV8: ${v8Version}\nOS: ${OSInfo}` - electron.dialog.showMessageBox(BrowserWindow.getFocusedWindow(), - { - title: 'BoostNote', - message: 'BoostNote', - type: 'info', - detail: `\n${detail}` - }) + electron.dialog.showMessageBox(BrowserWindow.getFocusedWindow(), { + title: 'BoostNote', + message: 'BoostNote', + type: 'info', + detail: `\n${detail}` + }) } } ] } -module.exports = process.platform === 'darwin' - ? [boost, file, edit, view, window, help] - : process.platform === 'win32' - ? [boost, file, view, help] - : [file, view, help] +module.exports = + process.platform === 'darwin' + ? [boost, file, edit, view, window, help] + : process.platform === 'win32' + ? [boost, file, view, help] + : [file, view, help] diff --git a/lib/main-window.js b/lib/main-window.js index 515dc8b4..e6b0cb29 100644 --- a/lib/main-window.js +++ b/lib/main-window.js @@ -54,12 +54,17 @@ const mainWindow = new BrowserWindow({ }, icon: path.resolve(__dirname, '../resources/app.png') }) -const url = path.resolve(__dirname, process.env.NODE_ENV === 'development' ? './main.development.html' : './main.production.html') +const url = path.resolve( + __dirname, + process.env.NODE_ENV === 'development' + ? './main.development.html' + : './main.production.html' +) mainWindow.loadURL('file://' + url) mainWindow.setMenuBarVisibility(false) -mainWindow.webContents.on('new-window', function (e) { +mainWindow.webContents.on('new-window', function(e) { e.preventDefault() }) @@ -74,10 +79,10 @@ mainWindow.webContents.sendInputEvent({ }) if (process.platform === 'darwin') { - mainWindow.on('close', function (e) { + mainWindow.on('close', function(e) { e.preventDefault() if (mainWindow.isFullScreen()) { - mainWindow.once('leave-full-screen', function () { + mainWindow.once('leave-full-screen', function() { mainWindow.hide() }) mainWindow.setFullScreen(false) @@ -86,7 +91,7 @@ if (process.platform === 'darwin') { } }) - app.on('before-quit', function (e) { + app.on('before-quit', function(e) { mainWindow.removeAllListeners() }) } @@ -94,7 +99,7 @@ if (process.platform === 'darwin') { mainWindow.on('resize', _.throttle(storeWindowSize, 500)) mainWindow.on('move', _.throttle(storeWindowSize, 500)) -function storeWindowSize () { +function storeWindowSize() { try { config.set('windowsize', mainWindow.getBounds()) } catch (e) { @@ -103,7 +108,7 @@ function storeWindowSize () { } } -app.on('activate', function () { +app.on('activate', function() { if (mainWindow == null) return null mainWindow.show() }) diff --git a/lib/touchbar-menu.js b/lib/touchbar-menu.js index 90a64410..b3696e13 100644 --- a/lib/touchbar-menu.js +++ b/lib/touchbar-menu.js @@ -1,5 +1,5 @@ -const {TouchBar} = require('electron') -const {TouchBarButton, TouchBarSpacer} = TouchBar +const { TouchBar } = require('electron') +const { TouchBarButton, TouchBarSpacer } = TouchBar const mainWindow = require('./main-window') const allNotes = new TouchBarButton({ @@ -35,7 +35,6 @@ module.exports = new TouchBar([ allNotes, starredNotes, trash, - new TouchBarSpacer({size: 'small'}), + new TouchBarSpacer({ size: 'small' }), newNote ]) - diff --git a/locales/cs.json b/locales/cs.json index 178659c3..9176fc28 100644 --- a/locales/cs.json +++ b/locales/cs.json @@ -9,6 +9,7 @@ "Toggle Mode": "Přepnout režim", "Add tag...": "Přidat štítek...", "Trash": "Koš", + "Ok": "Ok", "MODIFICATION DATE": "DATUM ZMĚNY", "Words": "Slova", "Letters": "Písmena", diff --git a/locales/da.json b/locales/da.json index 04520a4d..932b1939 100644 --- a/locales/da.json +++ b/locales/da.json @@ -8,6 +8,7 @@ "to create a new note": "to create a new note", "Toggle Mode": "Toggle Mode", "Trash": "Trash", + "Ok": "Ok", "MODIFICATION DATE": "MODIFICATION DATE", "Words": "Words", "Letters": "Letters", @@ -158,6 +159,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/de.json b/locales/de.json index 9f6d9b05..22f15957 100644 --- a/locales/de.json +++ b/locales/de.json @@ -8,6 +8,7 @@ "to create a new note": "um eine neue Notiz zu erstellen", "Toggle Mode": "Modus umschalten", "Trash": "Papierkorb", + "Ok": "Ok", "MODIFICATION DATE": "ÄNDERUNGSDATUM", "Words": "Wörter", "Letters": "Buchstaben", @@ -214,6 +215,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/ordner...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/en.json b/locales/en.json index 183bdaad..dcc9dd5b 100644 --- a/locales/en.json +++ b/locales/en.json @@ -9,6 +9,7 @@ "Toggle Mode": "Toggle Mode", "Add tag...": "Add tag...", "Trash": "Trash", + "Ok": "Ok", "MODIFICATION DATE": "MODIFICATION DATE", "Words": "Words", "Letters": "Letters", @@ -189,6 +190,7 @@ "New notes are tagged with the filtering tags": "New notes are tagged with the filtering tags", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note", "Enable Auto Update": "Enable Auto Update" diff --git a/locales/es-ES.json b/locales/es-ES.json index 34ae3e24..1495b6be 100644 --- a/locales/es-ES.json +++ b/locales/es-ES.json @@ -8,6 +8,7 @@ "to create a new note": "para crear una nueva nota", "Toggle Mode": "Alternar modo", "Trash": "Basura", + "Ok": "Ok", "MODIFICATION DATE": "FECHA DE MODIFICACIÓN", "Words": "Palabras", "Letters": "Letras", @@ -160,6 +161,7 @@ "Show menu bar": "Mostrar barra del menú", "Auto Detect": "Detección automática", "Snippet Default Language": "Lenguaje por defecto de los fragmentos de código", + "Filter tags/folders...": "filter etiquetas/carpeta...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/fa.json b/locales/fa.json index 92a40ee6..1ebd8789 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -8,6 +8,7 @@ "to create a new note": "برای ساخت یک یادداشت", "Toggle Mode": "تغییر حالت نمایش", "Trash": "سطل آشغال", + "Ok": "خوب", "MODIFICATION DATE": "تاریخ تغییر", "Words": "کلمات", "Letters": "حروف", @@ -162,6 +163,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/fr.json b/locales/fr.json index 14214687..122b6906 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -8,6 +8,7 @@ "to create a new note": "pour créer une nouvelle note", "Toggle Mode": "Toggle Mode", "Trash": "Poubelle", + "Ok": "Ok", "MODIFICATION DATE": "DATE DE MODIFICATION", "Words": "Mots", "Letters": "Lettres", @@ -174,6 +175,7 @@ "Snippet prefix": "Préfixe du snippet", "Delete Note": "Supprimer la note", "New notes are tagged with the filtering tags": "Les nouvelles notes sont taggées avec les tags de filtrage", + "Filter tags/folders...": "filtrage tags/dossier...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/hu.json b/locales/hu.json index 89229abb..3fa49b71 100644 --- a/locales/hu.json +++ b/locales/hu.json @@ -9,6 +9,7 @@ "Toggle Mode": "Mód Váltás", "Add tag...": "Tag hozzáadása...", "Trash": "Lomtár", + "Ok": "oké", "MODIFICATION DATE": "MÓDOSÍTÁS DÁTUMA", "Words": "Szó", "Letters": "Betű", @@ -182,6 +183,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/it.json b/locales/it.json index 45c09cd3..6998fb17 100644 --- a/locales/it.json +++ b/locales/it.json @@ -8,6 +8,7 @@ "to create a new note": "per creare una nuova nota", "Toggle Mode": "Cambia Modalità", "Trash": "Cestino", + "Ok": "Ok", "MODIFICATION DATE": "DATA DI MODIFICA", "Words": "Parole", "Letters": "Lettere", @@ -162,6 +163,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/ja.json b/locales/ja.json index bcd8d2d0..53abf7b0 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -11,6 +11,7 @@ "Star": "お気に入り", "Fullscreen": "全画面", "Trash": "ゴミ箱", + "Ok": "OK", "Info": "情報", "MODIFICATION DATE": "修正日", "Words": "ワード", @@ -221,6 +222,7 @@ "Spellcheck disabled": "スペルチェック無効", "Show menu bar": "メニューバーを表示", "Auto Detect": "自動検出", + "Filter tags/folders...": "タグ/フォルダをフィルタ...", "Enable HTML label in mermaid flowcharts": "mermaid flowchartでHTMLラベルを有効にする ⚠ このオプションには潜在的なXSSの危険性があります。", "Wrap line in Snippet Note": "行を右端で折り返す(Snippet Note)" } diff --git a/locales/ko.json b/locales/ko.json index 16623c49..1bb8c93d 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -8,6 +8,7 @@ "to create a new note": "to create a new note", "Toggle Mode": "모드 전환", "Trash": "쓰레기 통", + "Ok": "확인", "MODIFICATION DATE": "변경 날짜", "Words": "단어 수", "Letters": "글자 수", @@ -165,6 +166,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/no.json b/locales/no.json index b7c46089..1ed7fa49 100644 --- a/locales/no.json +++ b/locales/no.json @@ -158,6 +158,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/pl.json b/locales/pl.json index c32ea4bd..44d33831 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -8,6 +8,7 @@ "to create a new note": "Aby stworzyć nową notatkę", "Toggle Mode": "Przełącz tryb", "Trash": "Kosz", + "Ok": "dobrze", "MODIFICATION DATE": "DATA MODYFIKACJI", "Words": "Słowa", "Letters": "Litery", @@ -167,6 +168,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/pt-BR.json b/locales/pt-BR.json index 8b0bd478..ace5dfcc 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -8,6 +8,7 @@ "to create a new note": "para criar uma nova nota", "Toggle Mode": "Modo de alternância", "Trash": "Lixeira", + "Ok": "Ok", "MODIFICATION DATE": "DATA DE MODIFICAÇÃO", "Words": "Palavras", "Letters": "Letras", @@ -158,6 +159,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/pt-PT.json b/locales/pt-PT.json index 25bf82ae..a9e91bf8 100644 --- a/locales/pt-PT.json +++ b/locales/pt-PT.json @@ -8,6 +8,7 @@ "to create a new note": "para criar uma nova nota", "Toggle Mode": "Alternar Modo", "Trash": "Lixo", + "Ok": "Ok", "MODIFICATION DATE": "DATA DE MODIFICAÇÃO", "Words": "Palavras", "Letters": "Letras", @@ -157,6 +158,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/ru.json b/locales/ru.json index 1211c6b2..2a350f0e 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -8,6 +8,7 @@ "to create a new note": "создать новую запись", "Toggle Mode": "Переключить режим", "Trash": "Корзина", + "Ok": "Ok", "MODIFICATION DATE": "Дата изменения", "Words": "Слова", "Letters": "Буквы", @@ -155,6 +156,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/sq.json b/locales/sq.json index d88fb29e..fac4a6a6 100644 --- a/locales/sq.json +++ b/locales/sq.json @@ -8,6 +8,7 @@ "to create a new note": "to create a new note", "Toggle Mode": "Toggle Mode", "Trash": "Trash", + "Ok": "Ok", "MODIFICATION DATE": "MODIFICATION DATE", "Words": "Words", "Letters": "Letters", @@ -157,6 +158,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/th.json b/locales/th.json index f8c73b27..721493f9 100644 --- a/locales/th.json +++ b/locales/th.json @@ -9,6 +9,7 @@ "Toggle Mode": "Toggle Mode", "Add tag...": "เพิ่มแท็ก...", "Trash": "ถังขยะ", + "Ok": "ตกลง.", "MODIFICATION DATE": "แก้ไขเมื่อ", "Words": "คำ", "Letters": "ตัวอักษร", @@ -184,6 +185,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/tr.json b/locales/tr.json index 8a673cb1..ddb72609 100644 --- a/locales/tr.json +++ b/locales/tr.json @@ -8,6 +8,7 @@ "to create a new note": "yeni not oluşturmak için", "Toggle Mode": "Mod Değiştir", "Trash": "Çöp", + "Ok": "tamam", "MODIFICATION DATE": "DEĞİŞİKLİK TARİHİ", "Words": "Kelimeler", "Letters": "Harfler", @@ -157,6 +158,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/locales/zh-CN.json b/locales/zh-CN.json old mode 100644 new mode 100755 index c023708d..070b6633 --- a/locales/zh-CN.json +++ b/locales/zh-CN.json @@ -8,8 +8,9 @@ "to create a new note": "新建笔记", "Toggle Mode": "切换模式", "Trash": "废纸篓", + "Ok": "好", "MODIFICATION DATE": "更改时间", - "Words": "单词", + "Words": "单词数", "Letters": "字数", "STORAGE": "本地存储", "FOLDER": "文件夹", @@ -40,13 +41,13 @@ "Editor Font Size": "编辑器字号", "Editor Font Family": "编辑器字体", "Editor Indent Style": "编辑器缩进风格", - "Spaces": "空格", + "Spaces": "Spaces", "Tabs": "Tabs", "Switch to Preview": "快速切换到预览界面", - "When Editor Blurred": "当编辑器失去焦点的时候,切换到预览界面", + "When Editor Blurred": "当编辑器失去焦点的时候", "When Editor Blurred, Edit On Double Click": "当编辑器失去焦点的时候预览,双击切换到编辑界面", - "On Right Click": "右键点击切换两个界面", - "Editor Keymap": "编辑器 Keymap", + "On Right Click": "右键点击", + "Editor Keymap": "编辑器键位设置", "default": "默认", "vim": "vim", "emacs": "emacs", @@ -82,7 +83,7 @@ "Analytics": "分析", "Boostnote collects anonymous data for the sole purpose of improving the application, and strictly does not collect any personal information such the contents of your notes.": "Boostnote 收集匿名数据只为了提升软件使用体验,绝对不收集任何个人信息(包括笔记内容)", "You can see how it works on ": "你可以看看它的源码是如何运作的 ", - "You can choose to enable or disable this option.": "你可以选择开启或不开启这个功能", + "You can choose to enable or disable this option.": "你可以选择开启或禁用这个功能", "Enable analytics to help improve Boostnote": "允许对数据进行分析,帮助我们改进 Boostnote", "Crowdfunding": "众筹", "Dear Boostnote users,": "亲爱的用户:", @@ -132,7 +133,7 @@ "Restore": "恢复", "Permanent Delete": "永久删除", "Confirm note deletion": "确认删除笔记", - "This will permanently remove this note.": "永久地删除这条笔记", + "This will permanently remove this note.": "将永久地删除这条笔记", "Successfully applied!": "设置成功", "Albanian": "Albanian", "Chinese (zh-CN)": "简体中文", @@ -208,7 +209,7 @@ "Folder Name": "文件夹名称", "No tags": "无标签", "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "将文本箭头转换为完整符号。 ⚠ 注意这会影响 Markdown 的 HTML 注释。", - "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠", + "⚠ You have pasted a link referring an attachment that could not be found in the storage location of this note. Pasting links referring attachments is only supported if the source and destination location is the same storage. Please Drag&Drop the attachment instead! ⚠": "⚠ 您粘贴了一个链接,但在此文档的存储位置中找不到对应的附件。仅当附件和文档在同一存储位置时,才支持粘贴引用附件的链接。请将附件拖放到文档内!⚠", "Default New Note": "预设新笔记类型", "Show only related tags": "只显示相关标签", "Snippet Default Language": "程式码片段预设语言", @@ -219,9 +220,10 @@ "Custom CSS": "自定义 CSS", "Allow custom CSS for preview": "允许预览自定义 CSS", "Render newlines in Markdown paragraphs as
": "在 Markdown 段落中使用
换行", - "Spellcheck disabled": "Spellcheck disabled", + "Spellcheck disabled": "拼写检查已禁用", "Show menu bar": "显示菜单栏", - "Auto Detect": "Auto Detect", + "Auto Detect": "自动检测", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "在 mermaid flowcharts 中启用 HTML 标签 ⚠ 这个选项可能会产生 XSS", "Wrap line in Snippet Note": "在 Snippet Note 里换行", "Toggle Editor Mode": "切换编辑模式", @@ -231,7 +233,7 @@ "Show/Hide Menu Bar": "显示/隐藏 菜单栏", "Save tags of a note in alphabetical order": "按字母顺序存储标签", "Show tags of a note in alphabetical order": "按字母顺序显示标签", - "Enable live count of notes": "实时统计标签下笔记个数", + "Enable live count of notes": "实时统计标签下笔记数量", "New notes are tagged with the filtering tags": "新建的笔记带有在标签列表过滤的标签", "Front matter title field": "从 front-matter 中抽取标题的字段名", "Extract title from front matter": "启用从 front-matter 抽取标题", diff --git a/locales/zh-TW.json b/locales/zh-TW.json old mode 100644 new mode 100755 index 7ecc0087..f2e208dc --- a/locales/zh-TW.json +++ b/locales/zh-TW.json @@ -8,6 +8,7 @@ "to create a new note": "新增筆記", "Toggle Mode": "切換模式", "Trash": "垃圾桶", + "Ok": "好", "MODIFICATION DATE": "修改時間", "Words": "單字", "Letters": "字數", @@ -166,6 +167,7 @@ "Spellcheck disabled": "Spellcheck disabled", "Show menu bar": "Show menu bar", "Auto Detect": "Auto Detect", + "Filter tags/folders...": "filter tags/folders...", "Enable HTML label in mermaid flowcharts": "Enable HTML label in mermaid flowcharts ⚠ This option potentially has a risk of XSS.", "Wrap line in Snippet Note": "Wrap line in Snippet Note" } diff --git a/package.json b/package.json index c85bfaa0..75d12211 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "boost", "productName": "Boostnote", - "version": "0.13.0", + "version": "0.15.0", "main": "index.js", "description": "Boostnote", "license": "GPL-3.0", @@ -111,6 +111,7 @@ "react-composition-input": "^1.1.1", "react-debounce-render": "^4.0.1", "react-dom": "^16.8.6", + "react-emoji-render": "^1.1.0", "react-image-carousel": "^2.0.18", "react-redux": "^7.0.3", "react-router-dom": "^5.0.0", @@ -151,9 +152,11 @@ "electron-debug": "^2.2.0", "electron-devtools-installer": "^2.2.4", "electron-packager": "^12.2.0", - "eslint": "^3.13.1", + "eslint": "^4.18.2", + "eslint-config-prettier": "^6.10.0", "eslint-config-standard": "^6.2.1", "eslint-config-standard-jsx": "^3.2.0", + "eslint-plugin-prettier": "^3.1.2", "eslint-plugin-react": "^7.8.2", "eslint-plugin-standard": "^3.0.1", "faker": "^3.1.0", @@ -185,7 +188,7 @@ "webpack-dev-server": "^1.12.0" }, "optionalDependencies": { - "grunt-electron-installer-debian": "^0.2.0", + "grunt-electron-installer-debian": "^0.5.0", "grunt-electron-installer-redhat": "^0.3.1" }, "optional": false, diff --git a/prettier.config b/prettier.config index 8b8b7b99..66e7e941 100644 --- a/prettier.config +++ b/prettier.config @@ -1,6 +1,6 @@ { "trailingComma": "es5", - "tabWidth": 4, + "tabWidth": 2, "semi": false, "singleQuote": true } \ No newline at end of file diff --git a/readme.md b/readme.md index 648c78a7..c8f6e6a9 100644 --- a/readme.md +++ b/readme.md @@ -1,4 +1,4 @@ -:mega: The renewal will be released end of Nov, 2019. [To keep updated, subscribe our mailing list!](https://boostnote.io/#subscribe) +> [We've launched desktop app of the new Boost Note now. We'll release its mobile app too in January 2020.](https://github.com/BoostIO/BoostNote.next) ![Boostnote app screenshot](./resources/repository/top.png) @@ -11,10 +11,14 @@

+## Download + +[Find the latest release of Boostnote here!](https://github.com/BoostIO/boost-releases/releases/) + ## Authors & Maintainers - [Rokt33r](https://github.com/rokt33r) -- [Kazz](https://github.com/kazup01) +- [KZ](https://github.com/kazup01) - [ZeroX-DG](https://github.com/ZeroX-DG) ## Contributors @@ -39,9 +43,8 @@ Issues on Boostnote can be funded by anyone and the money will be distributed to #### More Information * Website: https://boostnote.io -* Newsletters: https://boostnote.io/#subscribe * [Development](https://github.com/BoostIO/Boostnote/blob/master/docs/build.md): Development configurations for Boostnote. -* Copyright (C) 2016 - 2019 BoostIO, Inc. +* Copyright (C) 2016 - 2020 BoostIO, Inc. #### License diff --git a/resources/icon/icon-left-to-right.svg b/resources/icon/icon-left-to-right.svg new file mode 100644 index 00000000..2489e763 --- /dev/null +++ b/resources/icon/icon-left-to-right.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/icon/icon-right-to-left.svg b/resources/icon/icon-right-to-left.svg new file mode 100644 index 00000000..da833c2d --- /dev/null +++ b/resources/icon/icon-right-to-left.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/resources/icon/icon-search-active.svg b/resources/icon/icon-search-active.svg new file mode 100644 index 00000000..f8cace73 --- /dev/null +++ b/resources/icon/icon-search-active.svg @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/icon/icon-search.svg b/resources/icon/icon-search.svg new file mode 100644 index 00000000..d2181a34 --- /dev/null +++ b/resources/icon/icon-search.svg @@ -0,0 +1,14 @@ + + + + + + + + \ No newline at end of file diff --git a/resources/icon/icon-up.svg b/resources/icon/icon-up.svg new file mode 100644 index 00000000..14f0257b --- /dev/null +++ b/resources/icon/icon-up.svg @@ -0,0 +1,17 @@ + + + + icon-down + Created with Sketch. + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/components/TagListItem.snapshot.test.js b/tests/components/TagListItem.snapshot.test.js index 637844e6..e128978d 100644 --- a/tests/components/TagListItem.snapshot.test.js +++ b/tests/components/TagListItem.snapshot.test.js @@ -3,7 +3,9 @@ import renderer from 'react-test-renderer' import TagListItem from 'browser/components/TagListItem' it('TagListItem renders correctly', () => { - const tagListItem = renderer.create() + const tagListItem = renderer.create( + + ) expect(tagListItem.toJSON()).toMatchSnapshot() }) diff --git a/tests/dataApi/addStorage.js b/tests/dataApi/addStorage.js index da39a993..81ddd5b3 100644 --- a/tests/dataApi/addStorage.js +++ b/tests/dataApi/addStorage.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -18,24 +21,24 @@ const v1StoragePath = path.join(os.tmpdir(), 'test/addStorage-v1-storage') // const legacyStoragePath = path.join(os.tmpdir(), 'test/addStorage-legacy-storage') // const emptyDirPath = path.join(os.tmpdir(), 'test/addStorage-empty-storage') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.v1StorageData = TestDummy.dummyStorage(v1StoragePath) // t.context.legacyStorageData = TestDummy.dummyLegacyStorage(legacyStoragePath) localStorage.setItem('storages', JSON.stringify([])) }) -test.serial('Add Storage', (t) => { +test.serial('Add Storage', t => { const input = { type: 'FILESYSTEM', name: 'add-storage1', path: v1StoragePath } return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return addStorage(input) }) - .then(function validateResult (data) { + .then(function validateResult(data) { const { storage, notes } = data // Check data.storage @@ -48,18 +51,22 @@ test.serial('Add Storage', (t) => { // Check data.notes t.is(notes.length, t.context.v1StorageData.notes.length) - notes.forEach(function validateNote (note) { + notes.forEach(function validateNote(note) { t.is(note.storage, storage.key) }) // Check localStorage - const cacheData = _.find(JSON.parse(localStorage.getItem('storages')), {key: data.storage.key}) + const cacheData = _.find(JSON.parse(localStorage.getItem('storages')), { + key: data.storage.key + }) t.is(cacheData.name, input.name) t.is(cacheData.type, input.type) t.is(cacheData.path, input.path) // Check boostnote.json - const jsonData = CSON.readFileSync(path.join(storage.path, 'boostnote.json')) + const jsonData = CSON.readFileSync( + path.join(storage.path, 'boostnote.json') + ) t.true(_.isArray(jsonData.folders)) t.is(jsonData.version, '1.0') t.is(jsonData.folders.length, t.context.v1StorageData.json.folders.length) diff --git a/tests/dataApi/attachmentManagement.test.js b/tests/dataApi/attachmentManagement.test.js index 13dcedca..e49556ca 100644 --- a/tests/dataApi/attachmentManagement.test.js +++ b/tests/dataApi/attachmentManagement.test.js @@ -13,48 +13,71 @@ const sander = require('sander') const systemUnderTest = require('browser/main/lib/dataApi/attachmentManagement') -it('should test that copyAttachment should throw an error if sourcePath or storageKey or noteKey are undefined', function () { - systemUnderTest.copyAttachment(undefined, 'storageKey').then(() => {}, error => { - expect(error).toBe('sourceFilePath has to be given') - }) - systemUnderTest.copyAttachment(null, 'storageKey', 'noteKey').then(() => {}, error => { - expect(error).toBe('sourceFilePath has to be given') - }) - systemUnderTest.copyAttachment('source', undefined, 'noteKey').then(() => {}, error => { - expect(error).toBe('storageKey has to be given') - }) - systemUnderTest.copyAttachment('source', null, 'noteKey').then(() => {}, error => { - expect(error).toBe('storageKey has to be given') - }) - systemUnderTest.copyAttachment('source', 'storageKey', null).then(() => {}, error => { - expect(error).toBe('noteKey has to be given') - }) - systemUnderTest.copyAttachment('source', 'storageKey', undefined).then(() => {}, error => { - expect(error).toBe('noteKey has to be given') - }) +it('should test that copyAttachment should throw an error if sourcePath or storageKey or noteKey are undefined', function() { + systemUnderTest.copyAttachment(undefined, 'storageKey').then( + () => {}, + error => { + expect(error).toBe('sourceFilePath has to be given') + } + ) + systemUnderTest.copyAttachment(null, 'storageKey', 'noteKey').then( + () => {}, + error => { + expect(error).toBe('sourceFilePath has to be given') + } + ) + systemUnderTest.copyAttachment('source', undefined, 'noteKey').then( + () => {}, + error => { + expect(error).toBe('storageKey has to be given') + } + ) + systemUnderTest.copyAttachment('source', null, 'noteKey').then( + () => {}, + error => { + expect(error).toBe('storageKey has to be given') + } + ) + systemUnderTest.copyAttachment('source', 'storageKey', null).then( + () => {}, + error => { + expect(error).toBe('noteKey has to be given') + } + ) + systemUnderTest.copyAttachment('source', 'storageKey', undefined).then( + () => {}, + error => { + expect(error).toBe('noteKey has to be given') + } + ) }) -it('should test that copyAttachment should throw an error if sourcePath dosen\'t exists', function () { +it("should test that copyAttachment should throw an error if sourcePath dosen't exists", function() { fs.existsSync = jest.fn() fs.existsSync.mockReturnValue(false) - return systemUnderTest.copyAttachment('path', 'storageKey', 'noteKey').then(() => {}, error => { - expect(error).toBe('source file does not exist') - expect(fs.existsSync).toHaveBeenCalledWith('path') - }) + return systemUnderTest.copyAttachment('path', 'storageKey', 'noteKey').then( + () => {}, + error => { + expect(error).toBe('source file does not exist') + expect(fs.existsSync).toHaveBeenCalledWith('path') + } + ) }) -it('should test that copyAttachment works correctly assuming correct working of fs', function () { +it('should test that copyAttachment works correctly assuming correct working of fs', function() { const dummyExtension = '.ext' const sourcePath = 'path' + dummyExtension const storageKey = 'storageKey' const noteKey = 'noteKey' const dummyUniquePath = 'dummyPath' - const dummyStorage = {path: 'dummyStoragePath'} + const dummyStorage = { path: 'dummyStoragePath' } const dummyReadStream = {} dummyReadStream.pipe = jest.fn() - dummyReadStream.on = jest.fn((event, callback) => { callback() }) + dummyReadStream.on = jest.fn((event, callback) => { + callback() + }) fs.existsSync = jest.fn() fs.existsSync.mockReturnValue(true) fs.createReadStream = jest.fn(() => dummyReadStream) @@ -64,26 +87,43 @@ it('should test that copyAttachment works correctly assuming correct working of findStorage.findStorage.mockReturnValue(dummyStorage) uniqueSlug.mockReturnValue(dummyUniquePath) - return systemUnderTest.copyAttachment(sourcePath, storageKey, noteKey).then( - function (newFileName) { + return systemUnderTest + .copyAttachment(sourcePath, storageKey, noteKey) + .then(function(newFileName) { expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(fs.createReadStream).toHaveBeenCalledWith(sourcePath) expect(fs.existsSync).toHaveBeenCalledWith(sourcePath) expect(fs.createReadStream().pipe).toHaveBeenCalled() - expect(fs.createWriteStream).toHaveBeenCalledWith(path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey, dummyUniquePath + dummyExtension)) + expect(fs.createWriteStream).toHaveBeenCalledWith( + path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + noteKey, + dummyUniquePath + dummyExtension + ) + ) expect(newFileName).toBe(dummyUniquePath + dummyExtension) }) }) -it('should test that copyAttachment creates a new folder if the attachment folder doesn\'t exist', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it("should test that copyAttachment creates a new folder if the attachment folder doesn't exist", function() { + const dummyStorage = { path: 'dummyStoragePath' } const noteKey = 'noteKey' - const attachmentFolderPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER) - const attachmentFolderNoteKyPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey) + const attachmentFolderPath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER + ) + const attachmentFolderNoteKyPath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + noteKey + ) const dummyReadStream = {} dummyReadStream.pipe = jest.fn() - dummyReadStream.on = jest.fn((event, callback) => { callback() }) + dummyReadStream.on = jest.fn((event, callback) => { + callback() + }) fs.createReadStream = jest.fn(() => dummyReadStream) fs.existsSync = jest.fn() fs.existsSync.mockReturnValueOnce(true) @@ -95,8 +135,9 @@ it('should test that copyAttachment creates a new folder if the attachment folde findStorage.findStorage.mockReturnValue(dummyStorage) uniqueSlug.mockReturnValue('dummyPath') - return systemUnderTest.copyAttachment('path', 'storageKey', 'noteKey').then( - function () { + return systemUnderTest + .copyAttachment('path', 'storageKey', 'noteKey') + .then(function() { expect(fs.existsSync).toHaveBeenCalledWith(attachmentFolderPath) expect(fs.mkdirSync).toHaveBeenCalledWith(attachmentFolderPath) expect(fs.existsSync).toHaveBeenLastCalledWith(attachmentFolderNoteKyPath) @@ -104,12 +145,14 @@ it('should test that copyAttachment creates a new folder if the attachment folde }) }) -it('should test that copyAttachment don\'t uses a random file name if not intended ', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it("should test that copyAttachment don't uses a random file name if not intended ", function() { + const dummyStorage = { path: 'dummyStoragePath' } const dummyReadStream = {} dummyReadStream.pipe = jest.fn() - dummyReadStream.on = jest.fn((event, callback) => { callback() }) + dummyReadStream.on = jest.fn((event, callback) => { + callback() + }) fs.createReadStream = jest.fn(() => dummyReadStream) fs.existsSync = jest.fn() fs.existsSync.mockReturnValueOnce(true) @@ -120,23 +163,28 @@ it('should test that copyAttachment don\'t uses a random file name if not intend findStorage.findStorage.mockReturnValue(dummyStorage) uniqueSlug.mockReturnValue('dummyPath') - return systemUnderTest.copyAttachment('path', 'storageKey', 'noteKey', false).then( - function (newFileName) { + return systemUnderTest + .copyAttachment('path', 'storageKey', 'noteKey', false) + .then(function(newFileName) { expect(newFileName).toBe('path') }) }) -it('should test that copyAttachment with url (with extension, without query)', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that copyAttachment with url (with extension, without query)', function() { + const dummyStorage = { path: 'dummyStoragePath' } const dummyReadStream = { pipe: jest.fn(), - on: jest.fn((event, callback) => { callback() }) + on: jest.fn((event, callback) => { + callback() + }) } fs.createReadStream = jest.fn(() => dummyReadStream) const dummyWriteStream = { - write: jest.fn((data, callback) => { callback() }) + write: jest.fn((data, callback) => { + callback() + }) } fs.createWriteStream = jest.fn(() => dummyWriteStream) @@ -155,23 +203,28 @@ it('should test that copyAttachment with url (with extension, without query)', f data: 'data:image/jpeg;base64,Ym9vc3Rub3Rl' } - return systemUnderTest.copyAttachment(sourcePath, 'storageKey', 'noteKey').then( - function (newFileName) { + return systemUnderTest + .copyAttachment(sourcePath, 'storageKey', 'noteKey') + .then(function(newFileName) { expect(newFileName).toBe('dummyPath.jpg') }) }) -it('should test that copyAttachment with url (with extension, with query)', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that copyAttachment with url (with extension, with query)', function() { + const dummyStorage = { path: 'dummyStoragePath' } const dummyReadStream = { pipe: jest.fn(), - on: jest.fn((event, callback) => { callback() }) + on: jest.fn((event, callback) => { + callback() + }) } fs.createReadStream = jest.fn(() => dummyReadStream) const dummyWriteStream = { - write: jest.fn((data, callback) => { callback() }) + write: jest.fn((data, callback) => { + callback() + }) } fs.createWriteStream = jest.fn(() => dummyWriteStream) @@ -190,23 +243,28 @@ it('should test that copyAttachment with url (with extension, with query)', func data: 'data:image/jpeg;base64,Ym9vc3Rub3Rl' } - return systemUnderTest.copyAttachment(sourcePath, 'storageKey', 'noteKey').then( - function (newFileName) { + return systemUnderTest + .copyAttachment(sourcePath, 'storageKey', 'noteKey') + .then(function(newFileName) { expect(newFileName).toBe('dummyPath.jpg') }) }) -it('should test that copyAttachment with url (without extension, without query)', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that copyAttachment with url (without extension, without query)', function() { + const dummyStorage = { path: 'dummyStoragePath' } const dummyReadStream = { pipe: jest.fn(), - on: jest.fn((event, callback) => { callback() }) + on: jest.fn((event, callback) => { + callback() + }) } fs.createReadStream = jest.fn(() => dummyReadStream) const dummyWriteStream = { - write: jest.fn((data, callback) => { callback() }) + write: jest.fn((data, callback) => { + callback() + }) } fs.createWriteStream = jest.fn(() => dummyWriteStream) @@ -225,23 +283,28 @@ it('should test that copyAttachment with url (without extension, without query)' data: 'data:image/jpeg;base64,Ym9vc3Rub3Rl' } - return systemUnderTest.copyAttachment(sourcePath, 'storageKey', 'noteKey').then( - function (newFileName) { + return systemUnderTest + .copyAttachment(sourcePath, 'storageKey', 'noteKey') + .then(function(newFileName) { expect(newFileName).toBe('dummyPath.png') }) }) -it('should test that copyAttachment with url (without extension, with query)', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that copyAttachment with url (without extension, with query)', function() { + const dummyStorage = { path: 'dummyStoragePath' } const dummyReadStream = { pipe: jest.fn(), - on: jest.fn((event, callback) => { callback() }) + on: jest.fn((event, callback) => { + callback() + }) } fs.createReadStream = jest.fn(() => dummyReadStream) const dummyWriteStream = { - write: jest.fn((data, callback) => { callback() }) + write: jest.fn((data, callback) => { + callback() + }) } fs.createWriteStream = jest.fn(() => dummyWriteStream) @@ -260,13 +323,14 @@ it('should test that copyAttachment with url (without extension, with query)', f data: 'data:image/jpeg;base64,Ym9vc3Rub3Rl' } - return systemUnderTest.copyAttachment(sourcePath, 'storageKey', 'noteKey').then( - function (newFileName) { + return systemUnderTest + .copyAttachment(sourcePath, 'storageKey', 'noteKey') + .then(function(newFileName) { expect(newFileName).toBe('dummyPath.png') }) }) -it('should replace the all ":storage" path with the actual storage path', function () { +it('should replace the all ":storage" path with the actual storage path', function() { const storageFolder = systemUnderTest.DESTINATION_FOLDER const noteKey = '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' const testInput = @@ -277,21 +341,41 @@ it('should replace the all ":storage" path with the actual storage path', functi ' \n' + '

Headline

\n' + '

\n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

\n' + '

\n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

\n' + '

\n' + - ' dummyImage2.jpg\n' + + ' dummyImage2.jpg\n' + '

\n' + '
\n' +
     '            \n' +
-    '            \n' +
+    '            \n' +
     '        
\n' + '
\n' +
     '            \n' +
-    '            \n' +
+    '            \n' +
     '        
\n' + ' \n' + '' @@ -304,21 +388,56 @@ it('should replace the all ":storage" path with the actual storage path', functi ' \n' + '

Headline

\n' + '

\n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

\n' + '

\n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

\n' + '

\n' + - ' dummyImage2.jpg\n' + + ' dummyImage2.jpg\n' + '

\n' + '
\n' +
     '            \n' +
-    '            \n' +
+    '            \n' +
     '        
\n' + '
\n' +
     '            \n' +
-    '            \n' +
+    '            \n' +
     '        
\n' + ' \n' + '' @@ -326,7 +445,7 @@ it('should replace the all ":storage" path with the actual storage path', functi expect(actual).toEqual(expectedOutput) }) -it('should replace the ":storage" path with the actual storage path when they have different path separators', function () { +it('should replace the ":storage" path with the actual storage path when they have different path separators', function() { const storageFolder = systemUnderTest.DESTINATION_FOLDER const noteKey = '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' const testInput = @@ -337,10 +456,18 @@ it('should replace the ":storage" path with the actual storage path when they ha ' \n' + '

Headline

\n' + '

\n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

\n' + '

\n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

\n' + ' \n' + '' @@ -353,10 +480,24 @@ it('should replace the ":storage" path with the actual storage path when they ha ' \n' + '

Headline

\n' + '

\n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

\n' + '

\n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

\n' + ' \n' + '' @@ -364,7 +505,7 @@ it('should replace the ":storage" path with the actual storage path when they ha expect(actual).toEqual(expectedOutput) }) -it('should test that generateAttachmentMarkdown works correct both with previews and without', function () { +it('should test that generateAttachmentMarkdown works correct both with previews and without', function() { const fileName = 'fileName' const path = 'path' let expected = `![${fileName}](${path})` @@ -375,55 +516,133 @@ it('should test that generateAttachmentMarkdown works correct both with previews expect(actual).toEqual(expected) }) -it('should test that migrateAttachments work when they have different path separators', function () { +it('should test that migrateAttachments work when they have different path separators', function() { sander.existsSync = jest.fn(() => true) const dummyStoragePath = 'dummyStoragePath' const imagesPath = path.join(dummyStoragePath, 'images') const attachmentsPath = path.join(dummyStoragePath, 'attachments') const noteKey = 'noteKey' - const testInput = '"# Test\n' + - '\n' + - '![Screenshot1](:storage' + path.win32.sep + '0.3b88d0dc.png)\n' + - '![Screenshot2](:storage' + path.posix.sep + '0.2cb8875c.pdf)"' + const testInput = + '"# Test\n' + + '\n' + + '![Screenshot1](:storage' + + path.win32.sep + + '0.3b88d0dc.png)\n' + + '![Screenshot2](:storage' + + path.posix.sep + + '0.2cb8875c.pdf)"' systemUnderTest.migrateAttachments(testInput, dummyStoragePath, noteKey) expect(sander.existsSync.mock.calls[0][0]).toBe(imagesPath) - expect(sander.existsSync.mock.calls[1][0]).toBe(path.join(imagesPath, '0.3b88d0dc.png')) - expect(sander.existsSync.mock.calls[2][0]).toBe(path.join(attachmentsPath, '0.3b88d0dc.png')) - expect(sander.existsSync.mock.calls[3][0]).toBe(path.join(imagesPath, '0.2cb8875c.pdf')) - expect(sander.existsSync.mock.calls[4][0]).toBe(path.join(attachmentsPath, '0.2cb8875c.pdf')) + expect(sander.existsSync.mock.calls[1][0]).toBe( + path.join(imagesPath, '0.3b88d0dc.png') + ) + expect(sander.existsSync.mock.calls[2][0]).toBe( + path.join(attachmentsPath, '0.3b88d0dc.png') + ) + expect(sander.existsSync.mock.calls[3][0]).toBe( + path.join(imagesPath, '0.2cb8875c.pdf') + ) + expect(sander.existsSync.mock.calls[4][0]).toBe( + path.join(attachmentsPath, '0.2cb8875c.pdf') + ) }) -it('should test that getAttachmentsInMarkdownContent finds all attachments when they have different path separators', function () { - const testInput = '"# Test\n' + +it('should test that getAttachmentsInMarkdownContent finds all attachments when they have different path separators', function() { + const testInput = + '"# Test\n' + '\n' + - '![Screenshot1](:storage' + path.win32.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.win32.sep + '0.3b88d0dc.png)\n' + - '![Screenshot2](:storage' + path.posix.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.posix.sep + '2cb8875c.pdf)\n' + - '![Screenshot3](:storage' + path.win32.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.posix.sep + 'bbf49b02.jpg)"' + '![Screenshot1](:storage' + + path.win32.sep + + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + + path.win32.sep + + '0.3b88d0dc.png)\n' + + '![Screenshot2](:storage' + + path.posix.sep + + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + + path.posix.sep + + '2cb8875c.pdf)\n' + + '![Screenshot3](:storage' + + path.win32.sep + + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + + path.posix.sep + + 'bbf49b02.jpg)"' const actual = systemUnderTest.getAttachmentsInMarkdownContent(testInput) - const expected = [':storage' + path.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.sep + '0.3b88d0dc.png', ':storage' + path.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.sep + '2cb8875c.pdf', ':storage' + path.sep + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + path.sep + 'bbf49b02.jpg'] + const expected = [ + ':storage' + + path.sep + + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + + path.sep + + '0.3b88d0dc.png', + ':storage' + + path.sep + + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + + path.sep + + '2cb8875c.pdf', + ':storage' + + path.sep + + '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' + + path.sep + + 'bbf49b02.jpg' + ] expect(actual).toEqual(expect.arrayContaining(expected)) }) -it('should test that getAbsolutePathsOfAttachmentsInContent returns all absolute paths', function () { +it('should test that getAbsolutePathsOfAttachmentsInContent returns all absolute paths', function() { const dummyStoragePath = 'dummyStoragePath' const noteKey = '9c9c4ba3-bc1e-441f-9866-c1e9a806e31c' - const testInput = '"# Test\n' + + const testInput = + '"# Test\n' + '\n' + - '![Screenshot1](:storage' + path.win32.sep + noteKey + path.win32.sep + '0.6r4zdgc22xp.png)\n' + - '![Screenshot2](:storage' + path.posix.sep + noteKey + path.posix.sep + '0.q2i4iw0fyx.pdf)\n' + - '![Screenshot3](:storage' + path.win32.sep + noteKey + path.posix.sep + 'd6c5ee92.jpg)"' + '![Screenshot1](:storage' + + path.win32.sep + + noteKey + + path.win32.sep + + '0.6r4zdgc22xp.png)\n' + + '![Screenshot2](:storage' + + path.posix.sep + + noteKey + + path.posix.sep + + '0.q2i4iw0fyx.pdf)\n' + + '![Screenshot3](:storage' + + path.win32.sep + + noteKey + + path.posix.sep + + 'd6c5ee92.jpg)"' - const actual = systemUnderTest.getAbsolutePathsOfAttachmentsInContent(testInput, dummyStoragePath) - const expected = [dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + noteKey + path.sep + '0.6r4zdgc22xp.png', - dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + noteKey + path.sep + '0.q2i4iw0fyx.pdf', - dummyStoragePath + path.sep + systemUnderTest.DESTINATION_FOLDER + path.sep + noteKey + path.sep + 'd6c5ee92.jpg'] + const actual = systemUnderTest.getAbsolutePathsOfAttachmentsInContent( + testInput, + dummyStoragePath + ) + const expected = [ + dummyStoragePath + + path.sep + + systemUnderTest.DESTINATION_FOLDER + + path.sep + + noteKey + + path.sep + + '0.6r4zdgc22xp.png', + dummyStoragePath + + path.sep + + systemUnderTest.DESTINATION_FOLDER + + path.sep + + noteKey + + path.sep + + '0.q2i4iw0fyx.pdf', + dummyStoragePath + + path.sep + + systemUnderTest.DESTINATION_FOLDER + + path.sep + + noteKey + + path.sep + + 'd6c5ee92.jpg' + ] expect(actual).toEqual(expect.arrayContaining(expected)) }) -it('should remove the all ":storage" and noteKey references', function () { +it('should remove the all ":storage" and noteKey references', function() { const storageFolder = systemUnderTest.DESTINATION_FOLDER const noteKey = 'noteKey' const testInput = @@ -434,13 +653,25 @@ it('should remove the all ":storage" and noteKey references', function () { ' \n' + '

Headline

\n' + '

\n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

\n' + '

\n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

\n' + '

\n' + - ' dummyImage2.jpg\n' + + ' dummyImage2.jpg\n' + '

\n' + ' \n' + '' @@ -452,62 +683,107 @@ it('should remove the all ":storage" and noteKey references', function () { ' \n' + '

Headline

\n' + '

\n' + - ' dummyImage.png\n' + + ' dummyImage.png\n' + '

\n' + '

\n' + - ' dummyPDF.pdf\n' + + ' dummyPDF.pdf\n' + '

\n' + '

\n' + - ' dummyImage2.jpg\n' + + ' dummyImage2.jpg\n' + '

\n' + ' \n' + '' - const actual = systemUnderTest.removeStorageAndNoteReferences(testInput, noteKey) + const actual = systemUnderTest.removeStorageAndNoteReferences( + testInput, + noteKey + ) expect(actual).toEqual(expectedOutput) }) -it('should make sure that "removeStorageAndNoteReferences" works with markdown content as well', function () { +it('should make sure that "removeStorageAndNoteReferences" works with markdown content as well', function() { const noteKey = 'noteKey' const testInput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + noteKey + path.win32.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + noteKey + path.posix.sep + 'pdf.pdf](pdf})' + '![' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + noteKey + + path.win32.sep + + 'image.jpg](imageName}) \n' + + '[' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + noteKey + + path.posix.sep + + 'pdf.pdf](pdf})' const expectedOutput = 'Test input' + - '![' + systemUnderTest.DESTINATION_FOLDER + path.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.DESTINATION_FOLDER + path.sep + 'pdf.pdf](pdf})' - const actual = systemUnderTest.removeStorageAndNoteReferences(testInput, noteKey) + '![' + + systemUnderTest.DESTINATION_FOLDER + + path.sep + + 'image.jpg](imageName}) \n' + + '[' + + systemUnderTest.DESTINATION_FOLDER + + path.sep + + 'pdf.pdf](pdf})' + const actual = systemUnderTest.removeStorageAndNoteReferences( + testInput, + noteKey + ) expect(actual).toEqual(expectedOutput) }) -it('should delete the correct attachment folder if a note is deleted', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should delete the correct attachment folder if a note is deleted', function() { + const dummyStorage = { path: 'dummyStoragePath' } const storageKey = 'storageKey' const noteKey = 'noteKey' findStorage.findStorage = jest.fn(() => dummyStorage) sander.rimrafSync = jest.fn() - const expectedPathToBeDeleted = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey) + const expectedPathToBeDeleted = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + noteKey + ) systemUnderTest.deleteAttachmentFolder(storageKey, noteKey) expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(sander.rimrafSync).toHaveBeenCalledWith(expectedPathToBeDeleted) }) -it('should test that deleteAttachmentsNotPresentInNote deletes all unreferenced attachments ', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that deleteAttachmentsNotPresentInNote deletes all unreferenced attachments ', function() { + const dummyStorage = { path: 'dummyStoragePath' } const noteKey = 'noteKey' const storageKey = 'storageKey' const markdownContent = '' const dummyFilesInFolder = ['file1.txt', 'file2.pdf', 'file3.jpg'] - const attachmentFolderPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey) + const attachmentFolderPath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + noteKey + ) findStorage.findStorage = jest.fn(() => dummyStorage) fs.existsSync = jest.fn(() => true) - fs.readdir = jest.fn((paht, callback) => callback(undefined, dummyFilesInFolder)) + fs.readdir = jest.fn((paht, callback) => + callback(undefined, dummyFilesInFolder) + ) fs.unlink = jest.fn() - systemUnderTest.deleteAttachmentsNotPresentInNote(markdownContent, storageKey, noteKey) + systemUnderTest.deleteAttachmentsNotPresentInNote( + markdownContent, + storageKey, + noteKey + ) expect(fs.existsSync).toHaveBeenLastCalledWith(attachmentFolderPath) expect(fs.readdir).toHaveBeenCalledTimes(1) expect(fs.readdir.mock.calls[0][0]).toBe(attachmentFolderPath) @@ -518,35 +794,59 @@ it('should test that deleteAttachmentsNotPresentInNote deletes all unreferenced fsUnlinkCallArguments.push(fs.unlink.mock.calls[i][0]) } - dummyFilesInFolder.forEach(function (file) { - expect(fsUnlinkCallArguments.includes(path.join(attachmentFolderPath, file))).toBe(true) + dummyFilesInFolder.forEach(function(file) { + expect( + fsUnlinkCallArguments.includes(path.join(attachmentFolderPath, file)) + ).toBe(true) }) }) -it('should test that deleteAttachmentsNotPresentInNote does not delete referenced attachments', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that deleteAttachmentsNotPresentInNote does not delete referenced attachments', function() { + const dummyStorage = { path: 'dummyStoragePath' } const noteKey = 'noteKey' const storageKey = 'storageKey' const dummyFilesInFolder = ['file1.txt', 'file2.pdf', 'file3.jpg'] - const markdownContent = systemUnderTest.generateAttachmentMarkdown('fileLabel', path.join(systemUnderTest.STORAGE_FOLDER_PLACEHOLDER, noteKey, dummyFilesInFolder[0]), false) - const attachmentFolderPath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, noteKey) + const markdownContent = systemUnderTest.generateAttachmentMarkdown( + 'fileLabel', + path.join( + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER, + noteKey, + dummyFilesInFolder[0] + ), + false + ) + const attachmentFolderPath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + noteKey + ) findStorage.findStorage = jest.fn(() => dummyStorage) fs.existsSync = jest.fn(() => true) - fs.readdir = jest.fn((paht, callback) => callback(undefined, dummyFilesInFolder)) + fs.readdir = jest.fn((paht, callback) => + callback(undefined, dummyFilesInFolder) + ) fs.unlink = jest.fn() - systemUnderTest.deleteAttachmentsNotPresentInNote(markdownContent, storageKey, noteKey) + systemUnderTest.deleteAttachmentsNotPresentInNote( + markdownContent, + storageKey, + noteKey + ) expect(fs.unlink).toHaveBeenCalledTimes(dummyFilesInFolder.length - 1) const fsUnlinkCallArguments = [] for (let i = 0; i < dummyFilesInFolder.length - 1; i++) { fsUnlinkCallArguments.push(fs.unlink.mock.calls[i][0]) } - expect(fsUnlinkCallArguments.includes(path.join(attachmentFolderPath, dummyFilesInFolder[0]))).toBe(false) + expect( + fsUnlinkCallArguments.includes( + path.join(attachmentFolderPath, dummyFilesInFolder[0]) + ) + ).toBe(false) }) -it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, storageKey or noteContent was null', function () { +it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, storageKey or noteContent was null', function() { const noteKey = null const storageKey = null const markdownContent = '' @@ -556,13 +856,17 @@ it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, fs.readdir = jest.fn() fs.unlink = jest.fn() - systemUnderTest.deleteAttachmentsNotPresentInNote(markdownContent, storageKey, noteKey) + systemUnderTest.deleteAttachmentsNotPresentInNote( + markdownContent, + storageKey, + noteKey + ) expect(fs.existsSync).not.toHaveBeenCalled() expect(fs.readdir).not.toHaveBeenCalled() expect(fs.unlink).not.toHaveBeenCalled() }) -it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, storageKey or noteContent was undefined', function () { +it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, storageKey or noteContent was undefined', function() { const noteKey = undefined const storageKey = undefined const markdownContent = '' @@ -572,47 +876,70 @@ it('should test that deleteAttachmentsNotPresentInNote does nothing if noteKey, fs.readdir = jest.fn() fs.unlink = jest.fn() - systemUnderTest.deleteAttachmentsNotPresentInNote(markdownContent, storageKey, noteKey) + systemUnderTest.deleteAttachmentsNotPresentInNote( + markdownContent, + storageKey, + noteKey + ) expect(fs.existsSync).not.toHaveBeenCalled() expect(fs.readdir).not.toHaveBeenCalled() expect(fs.unlink).not.toHaveBeenCalled() }) -it('should test that getAttachmentsPathAndStatus return null if noteKey, storageKey or noteContent was undefined', function () { +it('should test that getAttachmentsPathAndStatus return null if noteKey, storageKey or noteContent was undefined', function() { const noteKey = undefined const storageKey = undefined const markdownContent = '' - const result = systemUnderTest.getAttachmentsPathAndStatus(markdownContent, storageKey, noteKey) + const result = systemUnderTest.getAttachmentsPathAndStatus( + markdownContent, + storageKey, + noteKey + ) expect(result).toBeNull() }) -it('should test that getAttachmentsPathAndStatus return null if noteKey, storageKey or noteContent was null', function () { +it('should test that getAttachmentsPathAndStatus return null if noteKey, storageKey or noteContent was null', function() { const noteKey = null const storageKey = null const markdownContent = '' - const result = systemUnderTest.getAttachmentsPathAndStatus(markdownContent, storageKey, noteKey) + const result = systemUnderTest.getAttachmentsPathAndStatus( + markdownContent, + storageKey, + noteKey + ) expect(result).toBeNull() }) -it('should test that getAttachmentsPathAndStatus return the correct path and status for attachments', async function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that getAttachmentsPathAndStatus return the correct path and status for attachments', async function() { + const dummyStorage = { path: 'dummyStoragePath' } const noteKey = 'noteKey' const storageKey = 'storageKey' const markdownContent = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + noteKey + path.win32.sep + 'file2.pdf](file2.pdf) \n' + '![' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + noteKey + + path.win32.sep + + 'file2.pdf](file2.pdf) \n' const dummyFilesInFolder = ['file1.txt', 'file2.pdf', 'file3.jpg'] findStorage.findStorage = jest.fn(() => dummyStorage) fs.existsSync = jest.fn(() => true) - fs.readdir = jest.fn((paht, callback) => callback(undefined, dummyFilesInFolder)) + fs.readdir = jest.fn((paht, callback) => + callback(undefined, dummyFilesInFolder) + ) fs.unlink = jest.fn() const targetStorage = findStorage.findStorage(storageKey) - const attachments = await systemUnderTest.getAttachmentsPathAndStatus(markdownContent, storageKey, noteKey) + const attachments = await systemUnderTest.getAttachmentsPathAndStatus( + markdownContent, + storageKey, + noteKey + ) expect(attachments.length).toBe(3) expect(attachments[0].isInUse).toBe(false) expect(attachments[1].isInUse).toBe(true) @@ -644,7 +971,7 @@ it('should test that getAttachmentsPathAndStatus return the correct path and sta ) }) -it('should test that moveAttachments moves attachments only if the source folder existed', function () { +it('should test that moveAttachments moves attachments only if the source folder existed', function() { fse.existsSync = jest.fn(() => false) fse.moveSync = jest.fn() @@ -654,14 +981,24 @@ it('should test that moveAttachments moves attachments only if the source folder const newNoteKey = 'newNoteKey' const content = '' - const expectedSource = path.join(oldPath, systemUnderTest.DESTINATION_FOLDER, oldNoteKey) + const expectedSource = path.join( + oldPath, + systemUnderTest.DESTINATION_FOLDER, + oldNoteKey + ) - systemUnderTest.moveAttachments(oldPath, newPath, oldNoteKey, newNoteKey, content) + systemUnderTest.moveAttachments( + oldPath, + newPath, + oldNoteKey, + newNoteKey, + content + ) expect(fse.existsSync).toHaveBeenCalledWith(expectedSource) expect(fse.moveSync).not.toHaveBeenCalled() }) -it('should test that moveAttachments moves attachments to the right destination', function () { +it('should test that moveAttachments moves attachments to the right destination', function() { fse.existsSync = jest.fn(() => true) fse.moveSync = jest.fn() @@ -671,15 +1008,29 @@ it('should test that moveAttachments moves attachments to the right destination' const newNoteKey = 'newNoteKey' const content = '' - const expectedSource = path.join(oldPath, systemUnderTest.DESTINATION_FOLDER, oldNoteKey) - const expectedDestination = path.join(newPath, systemUnderTest.DESTINATION_FOLDER, newNoteKey) + const expectedSource = path.join( + oldPath, + systemUnderTest.DESTINATION_FOLDER, + oldNoteKey + ) + const expectedDestination = path.join( + newPath, + systemUnderTest.DESTINATION_FOLDER, + newNoteKey + ) - systemUnderTest.moveAttachments(oldPath, newPath, oldNoteKey, newNoteKey, content) + systemUnderTest.moveAttachments( + oldPath, + newPath, + oldNoteKey, + newNoteKey, + content + ) expect(fse.existsSync).toHaveBeenCalledWith(expectedSource) expect(fse.moveSync).toHaveBeenCalledWith(expectedSource, expectedDestination) }) -it('should test that moveAttachments returns a correct modified content version', function () { +it('should test that moveAttachments returns a correct modified content version', function() { fse.existsSync = jest.fn() fse.moveSync = jest.fn() @@ -689,63 +1040,159 @@ it('should test that moveAttachments returns a correct modified content version' const newNoteKey = 'newNoteKey' const testInput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + oldNoteKey + path.win32.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + oldNoteKey + path.posix.sep + 'pdf.pdf](pdf})' + '![' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + oldNoteKey + + path.win32.sep + + 'image.jpg](imageName}) \n' + + '[' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + oldNoteKey + + path.posix.sep + + 'pdf.pdf](pdf})' const expectedOutput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + 'pdf.pdf](pdf})' + '![' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNoteKey + + path.sep + + 'image.jpg](imageName}) \n' + + '[' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNoteKey + + path.sep + + 'pdf.pdf](pdf})' - const actualContent = systemUnderTest.moveAttachments(oldPath, newPath, oldNoteKey, newNoteKey, testInput) + const actualContent = systemUnderTest.moveAttachments( + oldPath, + newPath, + oldNoteKey, + newNoteKey, + testInput + ) expect(actualContent).toBe(expectedOutput) }) -it('should test that cloneAttachments modifies the content of the new note correctly', function () { - const oldNote = {key: 'oldNoteKey', content: 'oldNoteContent', storage: 'storageKey', type: 'MARKDOWN_NOTE'} - const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKey', type: 'MARKDOWN_NOTE'} +it('should test that cloneAttachments modifies the content of the new note correctly', function() { + const oldNote = { + key: 'oldNoteKey', + content: 'oldNoteContent', + storage: 'storageKey', + type: 'MARKDOWN_NOTE' + } + const newNote = { + key: 'newNoteKey', + content: 'oldNoteContent', + storage: 'storageKey', + type: 'MARKDOWN_NOTE' + } const testInput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + oldNote.key + path.win32.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + oldNote.key + path.posix.sep + 'pdf.pdf](pdf})' + '![' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + oldNote.key + + path.win32.sep + + 'image.jpg](imageName}) \n' + + '[' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + oldNote.key + + path.posix.sep + + 'pdf.pdf](pdf})' newNote.content = testInput findStorage.findStorage = jest.fn() - findStorage.findStorage.mockReturnValue({path: 'dummyStoragePath'}) + findStorage.findStorage.mockReturnValue({ path: 'dummyStoragePath' }) const expectedOutput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNote.key + path.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNote.key + path.sep + 'pdf.pdf](pdf})' + '![' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNote.key + + path.sep + + 'image.jpg](imageName}) \n' + + '[' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNote.key + + path.sep + + 'pdf.pdf](pdf})' systemUnderTest.cloneAttachments(oldNote, newNote) expect(newNote.content).toBe(expectedOutput) }) -it('should test that cloneAttachments finds all attachments and copies them to the new location', function () { +it('should test that cloneAttachments finds all attachments and copies them to the new location', function() { const storagePathOld = 'storagePathOld' const storagePathNew = 'storagePathNew' - const dummyStorageOld = {path: storagePathOld} - const dummyStorageNew = {path: storagePathNew} - const oldNote = {key: 'oldNoteKey', content: 'oldNoteContent', storage: 'storageKeyOldNote', type: 'MARKDOWN_NOTE'} - const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKeyNewNote', type: 'MARKDOWN_NOTE'} + const dummyStorageOld = { path: storagePathOld } + const dummyStorageNew = { path: storagePathNew } + const oldNote = { + key: 'oldNoteKey', + content: 'oldNoteContent', + storage: 'storageKeyOldNote', + type: 'MARKDOWN_NOTE' + } + const newNote = { + key: 'newNoteKey', + content: 'oldNoteContent', + storage: 'storageKeyNewNote', + type: 'MARKDOWN_NOTE' + } const testInput = 'Test input' + - '![' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + oldNote.key + path.win32.sep + 'image.jpg](imageName}) \n' + - '[' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + oldNote.key + path.posix.sep + 'pdf.pdf](pdf})' + '![' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + oldNote.key + + path.win32.sep + + 'image.jpg](imageName}) \n' + + '[' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + oldNote.key + + path.posix.sep + + 'pdf.pdf](pdf})' oldNote.content = testInput newNote.content = testInput - const copyFileSyncResp = {to: jest.fn()} + const copyFileSyncResp = { to: jest.fn() } sander.copyFileSync = jest.fn() sander.copyFileSync.mockReturnValue(copyFileSyncResp) findStorage.findStorage = jest.fn() findStorage.findStorage.mockReturnValueOnce(dummyStorageOld) findStorage.findStorage.mockReturnValue(dummyStorageNew) - const pathAttachmentOneFrom = path.join(storagePathOld, systemUnderTest.DESTINATION_FOLDER, oldNote.key, 'image.jpg') - const pathAttachmentOneTo = path.join(storagePathNew, systemUnderTest.DESTINATION_FOLDER, newNote.key, 'image.jpg') + const pathAttachmentOneFrom = path.join( + storagePathOld, + systemUnderTest.DESTINATION_FOLDER, + oldNote.key, + 'image.jpg' + ) + const pathAttachmentOneTo = path.join( + storagePathNew, + systemUnderTest.DESTINATION_FOLDER, + newNote.key, + 'image.jpg' + ) - const pathAttachmentTwoFrom = path.join(storagePathOld, systemUnderTest.DESTINATION_FOLDER, oldNote.key, 'pdf.pdf') - const pathAttachmentTwoTo = path.join(storagePathNew, systemUnderTest.DESTINATION_FOLDER, newNote.key, 'pdf.pdf') + const pathAttachmentTwoFrom = path.join( + storagePathOld, + systemUnderTest.DESTINATION_FOLDER, + oldNote.key, + 'pdf.pdf' + ) + const pathAttachmentTwoTo = path.join( + storagePathNew, + systemUnderTest.DESTINATION_FOLDER, + newNote.key, + 'pdf.pdf' + ) systemUnderTest.cloneAttachments(oldNote, newNote) @@ -759,9 +1206,19 @@ it('should test that cloneAttachments finds all attachments and copies them to t expect(copyFileSyncResp.to.mock.calls[1][0]).toBe(pathAttachmentTwoTo) }) -it('should test that cloneAttachments finds all attachments and copies them to the new location', function () { - const oldNote = {key: 'oldNoteKey', content: 'oldNoteContent', storage: 'storageKeyOldNote', type: 'SOMETHING_ELSE'} - const newNote = {key: 'newNoteKey', content: 'oldNoteContent', storage: 'storageKeyNewNote', type: 'SOMETHING_ELSE'} +it('should test that cloneAttachments finds all attachments and copies them to the new location', function() { + const oldNote = { + key: 'oldNoteKey', + content: 'oldNoteContent', + storage: 'storageKeyOldNote', + type: 'SOMETHING_ELSE' + } + const newNote = { + key: 'newNoteKey', + content: 'oldNoteContent', + storage: 'storageKeyNewNote', + type: 'SOMETHING_ELSE' + } const testInput = 'Test input' oldNote.content = testInput newNote.content = testInput @@ -775,82 +1232,274 @@ it('should test that cloneAttachments finds all attachments and copies them to t expect(sander.copyFileSync).not.toHaveBeenCalled() }) -it('should test that isAttachmentLink works correctly', function () { +it('should test that isAttachmentLink works correctly', function() { expect(systemUnderTest.isAttachmentLink('text')).toBe(false) expect(systemUnderTest.isAttachmentLink('text [linkText](link)')).toBe(false) expect(systemUnderTest.isAttachmentLink('text ![linkText](link)')).toBe(false) - expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf )')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text [ linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text ![linkText ](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text [linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text ![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + 'noteKey' + path.win32.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf )')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text [ linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text ![linkText ](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf)')).toBe(true) - expect(systemUnderTest.isAttachmentLink('[linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text [linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf) test')).toBe(true) - expect(systemUnderTest.isAttachmentLink('text ![linkText](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + 'noteKey' + path.posix.sep + 'pdf.pdf) test')).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '[linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf)' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '![linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf )' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text [ linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf)' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text ![linkText ](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf)' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '[linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf) test' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '![linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf) test' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text [linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf) test' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text ![linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + 'noteKey' + + path.win32.sep + + 'pdf.pdf) test' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '[linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf)' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '![linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf )' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text [ linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf)' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text ![linkText ](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf)' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '[linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf) test' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + '![linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf) test' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text [linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf) test' + ) + ).toBe(true) + expect( + systemUnderTest.isAttachmentLink( + 'text ![linkText](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + 'noteKey' + + path.posix.sep + + 'pdf.pdf) test' + ) + ).toBe(true) }) -it('should test that handleAttachmentLinkPaste copies the attachments to the new location', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste copies the attachments to the new location', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'pdf.pdf)' const storageKey = 'storageKey' - const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') + const expectedSourceFilePath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'pdf.pdf' + ) sander.exists = jest.fn(() => Promise.resolve(true)) - systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve('dummyNewFileName')) + systemUnderTest.copyAttachment = jest.fn(() => + Promise.resolve('dummyNewFileName') + ) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) .then(() => { expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePath) - expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith(expectedSourceFilePath, storageKey, newNoteKey) + expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith( + expectedSourceFilePath, + storageKey, + newNoteKey + ) }) }) -it('should test that handleAttachmentLinkPaste copies the attachments to the new location - win32 path', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste copies the attachments to the new location - win32 path', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'pdf.pdf)' const storageKey = 'storageKey' - const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') + const expectedSourceFilePath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'pdf.pdf' + ) sander.exists = jest.fn(() => Promise.resolve(true)) - systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve('dummyNewFileName')) + systemUnderTest.copyAttachment = jest.fn(() => + Promise.resolve('dummyNewFileName') + ) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) .then(() => { expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePath) - expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith(expectedSourceFilePath, storageKey, newNoteKey) + expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith( + expectedSourceFilePath, + storageKey, + newNoteKey + ) }) }) -it('should test that handleAttachmentLinkPaste don\'t try to copy the file if it does not exist - win32 path', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it("should test that handleAttachmentLinkPaste don't try to copy the file if it does not exist - win32 path", function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'pdf.pdf)' const storageKey = 'storageKey' - const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') + const expectedSourceFilePath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'pdf.pdf' + ) sander.exists = jest.fn(() => Promise.resolve(false)) systemUnderTest.copyAttachment = jest.fn() systemUnderTest.generateFileNotFoundMarkdown = jest.fn() - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) .then(() => { expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePath) @@ -858,20 +1507,32 @@ it('should test that handleAttachmentLinkPaste don\'t try to copy the file if it }) }) -it('should test that handleAttachmentLinkPaste don\'t try to copy the file if it does not exist -- posix', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it("should test that handleAttachmentLinkPaste don't try to copy the file if it does not exist -- posix", function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'pdf.pdf)' const storageKey = 'storageKey' - const expectedSourceFilePath = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') + const expectedSourceFilePath = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'pdf.pdf' + ) sander.exists = jest.fn(() => Promise.resolve(false)) systemUnderTest.copyAttachment = jest.fn() systemUnderTest.generateFileNotFoundMarkdown = jest.fn() - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) .then(() => { expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePath) @@ -879,106 +1540,217 @@ it('should test that handleAttachmentLinkPaste don\'t try to copy the file if it }) }) -it('should test that handleAttachmentLinkPaste copies multiple attachments if multiple were pasted', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste copies multiple attachments if multiple were pasted', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf) ..' + - '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'img.jpg)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'pdf.pdf) ..' + + '![secondAttachment](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'img.jpg)' const storageKey = 'storageKey' - const expectedSourceFilePathOne = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') - const expectedSourceFilePathTwo = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'img.jpg') + const expectedSourceFilePathOne = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'pdf.pdf' + ) + const expectedSourceFilePathTwo = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'img.jpg' + ) sander.exists = jest.fn(() => Promise.resolve(true)) - systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve('dummyNewFileName')) + systemUnderTest.copyAttachment = jest.fn(() => + Promise.resolve('dummyNewFileName') + ) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) .then(() => { expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePathOne) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePathTwo) - expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith(expectedSourceFilePathOne, storageKey, newNoteKey) - expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith(expectedSourceFilePathTwo, storageKey, newNoteKey) + expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith( + expectedSourceFilePathOne, + storageKey, + newNoteKey + ) + expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith( + expectedSourceFilePathTwo, + storageKey, + newNoteKey + ) }) }) -it('should test that handleAttachmentLinkPaste returns the correct modified paste text', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste returns the correct modified paste text', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' const dummyNewFileName = 'dummyNewFileName' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf)' - const expectedText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyNewFileName + ')' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.win32.sep + + 'pdf.pdf)' + const expectedText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNoteKey + + path.sep + + dummyNewFileName + + ')' const storageKey = 'storageKey' sander.exists = jest.fn(() => Promise.resolve(true)) - systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve(dummyNewFileName)) + systemUnderTest.copyAttachment = jest.fn(() => + Promise.resolve(dummyNewFileName) + ) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) - .then((returnedPastedText) => { + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + .then(returnedPastedText => { expect(returnedPastedText).toBe(expectedText) }) }) -it('should test that handleAttachmentLinkPaste returns the correct modified paste text if multiple links are posted', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste returns the correct modified paste text if multiple links are posted', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' const dummyNewFileNameOne = 'dummyNewFileName' const dummyNewFileNameTwo = 'dummyNewFileNameTwo' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf) ' + - '![secondImage](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'img.jpg)' - const expectedText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyNewFileNameOne + ') ' + - '![secondImage](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyNewFileNameTwo + ')' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'pdf.pdf) ' + + '![secondImage](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'img.jpg)' + const expectedText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNoteKey + + path.sep + + dummyNewFileNameOne + + ') ' + + '![secondImage](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNoteKey + + path.sep + + dummyNewFileNameTwo + + ')' const storageKey = 'storageKey' sander.exists = jest.fn(() => Promise.resolve(true)) systemUnderTest.copyAttachment = jest.fn() - systemUnderTest.copyAttachment.mockReturnValueOnce(Promise.resolve(dummyNewFileNameOne)) - systemUnderTest.copyAttachment.mockReturnValue(Promise.resolve(dummyNewFileNameTwo)) + systemUnderTest.copyAttachment.mockReturnValueOnce( + Promise.resolve(dummyNewFileNameOne) + ) + systemUnderTest.copyAttachment.mockReturnValue( + Promise.resolve(dummyNewFileNameTwo) + ) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) - .then((returnedPastedText) => { + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + .then(returnedPastedText => { expect(returnedPastedText).toBe(expectedText) }) }) -it('should test that handleAttachmentLinkPaste calls the copy method correct if multiple links are posted where one file was found and one was not', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste calls the copy method correct if multiple links are posted where one file was found and one was not', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf) ..' + - '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'img.jpg)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'pdf.pdf) ..' + + '![secondAttachment](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'img.jpg)' const storageKey = 'storageKey' - const expectedSourceFilePathOne = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'pdf.pdf') - const expectedSourceFilePathTwo = path.join(dummyStorage.path, systemUnderTest.DESTINATION_FOLDER, pastedNoteKey, 'img.jpg') + const expectedSourceFilePathOne = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'pdf.pdf' + ) + const expectedSourceFilePathTwo = path.join( + dummyStorage.path, + systemUnderTest.DESTINATION_FOLDER, + pastedNoteKey, + 'img.jpg' + ) sander.exists = jest.fn() sander.exists.mockReturnValueOnce(Promise.resolve(false)) sander.exists.mockReturnValue(Promise.resolve(true)) - systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve('dummyNewFileName')) + systemUnderTest.copyAttachment = jest.fn(() => + Promise.resolve('dummyNewFileName') + ) systemUnderTest.generateFileNotFoundMarkdown = jest.fn() - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) .then(() => { expect(findStorage.findStorage).toHaveBeenCalledWith(storageKey) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePathOne) expect(sander.exists).toHaveBeenCalledWith(expectedSourceFilePathTwo) expect(systemUnderTest.copyAttachment).toHaveBeenCalledTimes(1) - expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith(expectedSourceFilePathTwo, storageKey, newNoteKey) + expect(systemUnderTest.copyAttachment).toHaveBeenCalledWith( + expectedSourceFilePathTwo, + storageKey, + newNoteKey + ) }) }) -it('should test that handleAttachmentLinkPaste returns the correct modified paste text if the file was not found', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste returns the correct modified paste text if the file was not found', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt.png](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf)' + const pasteText = + 'text ![alt.png](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.posix.sep + + 'pdf.pdf)' const storageKey = 'storageKey' const fileNotFoundMD = 'file not found' const expectedPastText = 'text ' + fileNotFoundMD @@ -986,19 +1758,31 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past systemUnderTest.generateFileNotFoundMarkdown = jest.fn(() => fileNotFoundMD) sander.exists = jest.fn(() => Promise.resolve(false)) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) - .then((returnedPastedText) => { + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + .then(returnedPastedText => { expect(returnedPastedText).toBe(expectedPastText) }) }) -it('should test that handleAttachmentLinkPaste returns the correct modified paste text if multiple files were not found', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste returns the correct modified paste text if multiple files were not found', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf) ' + - '![secondImage](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'img.jpg)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'pdf.pdf) ' + + '![secondImage](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'img.jpg)' const storageKey = 'storageKey' const fileNotFoundMD = 'file not found' const expectedPastText = 'text ' + fileNotFoundMD + ' ' + fileNotFoundMD @@ -1006,56 +1790,102 @@ it('should test that handleAttachmentLinkPaste returns the correct modified past sander.exists = jest.fn(() => Promise.resolve(false)) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) - .then((returnedPastedText) => { + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + .then(returnedPastedText => { expect(returnedPastedText).toBe(expectedPastText) }) }) -it('should test that handleAttachmentLinkPaste returns the correct modified paste text if one file was found and one was not found', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste returns the correct modified paste text if one file was found and one was not found', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' const dummyFoundFileName = 'dummyFileName' const fileNotFoundMD = 'file not found' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'pdf.pdf) .. ' + - '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'img.jpg)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'pdf.pdf) .. ' + + '![secondAttachment](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'img.jpg)' const storageKey = 'storageKey' - const expectedPastText = 'text ' + fileNotFoundMD + ' .. ![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyFoundFileName + ')' + const expectedPastText = + 'text ' + + fileNotFoundMD + + ' .. ![secondAttachment](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNoteKey + + path.sep + + dummyFoundFileName + + ')' sander.exists = jest.fn() sander.exists.mockReturnValueOnce(Promise.resolve(false)) sander.exists.mockReturnValue(Promise.resolve(true)) - systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve(dummyFoundFileName)) + systemUnderTest.copyAttachment = jest.fn(() => + Promise.resolve(dummyFoundFileName) + ) systemUnderTest.generateFileNotFoundMarkdown = jest.fn(() => fileNotFoundMD) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) - .then((returnedPastedText) => { + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + .then(returnedPastedText => { expect(returnedPastedText).toBe(expectedPastText) }) }) -it('should test that handleAttachmentLinkPaste returns the correct modified paste text if one file was found and one was not found', function () { - const dummyStorage = {path: 'dummyStoragePath'} +it('should test that handleAttachmentLinkPaste returns the correct modified paste text if one file was found and one was not found', function() { + const dummyStorage = { path: 'dummyStoragePath' } findStorage.findStorage = jest.fn(() => dummyStorage) const pastedNoteKey = 'b1e06f81-8266-49b9-b438-084003c2e723' const newNoteKey = 'abc234-8266-49b9-b438-084003c2e723' const dummyFoundFileName = 'dummyFileName' const fileNotFoundMD = 'file not found' - const pasteText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.posix.sep + pastedNoteKey + path.posix.sep + 'pdf.pdf) .. ' + - '![secondAttachment](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.win32.sep + pastedNoteKey + path.win32.sep + 'img.jpg)' + const pasteText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.posix.sep + + pastedNoteKey + + path.posix.sep + + 'pdf.pdf) .. ' + + '![secondAttachment](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.win32.sep + + pastedNoteKey + + path.win32.sep + + 'img.jpg)' const storageKey = 'storageKey' - const expectedPastText = 'text ![alt](' + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + path.sep + newNoteKey + path.sep + dummyFoundFileName + ') .. ' + fileNotFoundMD + const expectedPastText = + 'text ![alt](' + + systemUnderTest.STORAGE_FOLDER_PLACEHOLDER + + path.sep + + newNoteKey + + path.sep + + dummyFoundFileName + + ') .. ' + + fileNotFoundMD sander.exists = jest.fn() sander.exists.mockReturnValueOnce(Promise.resolve(true)) sander.exists.mockReturnValue(Promise.resolve(false)) - systemUnderTest.copyAttachment = jest.fn(() => Promise.resolve(dummyFoundFileName)) + systemUnderTest.copyAttachment = jest.fn(() => + Promise.resolve(dummyFoundFileName) + ) systemUnderTest.generateFileNotFoundMarkdown = jest.fn(() => fileNotFoundMD) - return systemUnderTest.handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) - .then((returnedPastedText) => { + return systemUnderTest + .handleAttachmentLinkPaste(storageKey, newNoteKey, pasteText) + .then(returnedPastedText => { expect(returnedPastedText).toBe(expectedPastText) }) }) diff --git a/tests/dataApi/copyFile-test.js b/tests/dataApi/copyFile-test.js index 533b1354..f38f1ed2 100644 --- a/tests/dataApi/copyFile-test.js +++ b/tests/dataApi/copyFile-test.js @@ -13,13 +13,13 @@ const srcPath = path.join(srcFolder, testFile) const dstFolder = path.join(__dirname, '😇') const dstPath = path.join(dstFolder, testFile) -test.before((t) => { +test.before(t => { if (!fs.existsSync(srcFolder)) fs.mkdirSync(srcFolder) fs.writeFileSync(srcPath, 'test') }) -test('`copyFile` should handle encoded URI on src path', (t) => { +test('`copyFile` should handle encoded URI on src path', t => { return copyFile(encodeURI(srcPath), dstPath) .then(() => { t.true(true) @@ -29,10 +29,9 @@ test('`copyFile` should handle encoded URI on src path', (t) => { }) }) -test.after((t) => { +test.after(t => { fs.unlinkSync(srcPath) fs.unlinkSync(dstPath) execSync(removeDirCommand + '"' + srcFolder + '"') execSync(removeDirCommand + '"' + dstFolder + '"') }) - diff --git a/tests/dataApi/createFolder-test.js b/tests/dataApi/createFolder-test.js index 1b9a1c83..fd54ba27 100644 --- a/tests/dataApi/createFolder-test.js +++ b/tests/dataApi/createFolder-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const _ = require('lodash') const TestDummy = require('../fixtures/TestDummy') @@ -16,30 +19,32 @@ const CSON = require('@rokt33r/season') const storagePath = path.join(os.tmpdir(), 'test/create-folder') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Create a folder', (t) => { +test.serial('Create a folder', t => { const storageKey = t.context.storage.cache.key const input = { name: 'created', color: '#ff5555' } return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return createFolder(storageKey, input) }) - .then(function assert (data) { + .then(function assert(data) { t.true(_.find(data.storage.folders, input) != null) - const jsonData = CSON.readFileSync(path.join(data.storage.path, 'boostnote.json')) + const jsonData = CSON.readFileSync( + path.join(data.storage.path, 'boostnote.json') + ) console.log(path.join(data.storage.path, 'boostnote.json')) t.true(_.find(jsonData.folders, input) != null) }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/createNote-test.js b/tests/dataApi/createNote-test.js index 3606dfd4..2c3af348 100644 --- a/tests/dataApi/createNote-test.js +++ b/tests/dataApi/createNote-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -16,26 +19,30 @@ const faker = require('faker') const storagePath = path.join(os.tmpdir(), 'test/create-note') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Create a note', (t) => { +test.serial('Create a note', t => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key - const randLinesHighlightedArray = new Array(10).fill().map(() => Math.round(Math.random() * 10)) + const randLinesHighlightedArray = new Array(10) + .fill() + .map(() => Math.round(Math.random() * 10)) const input1 = { type: 'SNIPPET_NOTE', description: faker.lorem.lines(), - snippets: [{ - name: faker.system.fileName(), - mode: 'text', - content: faker.lorem.lines(), - linesHighlighted: randLinesHighlightedArray - }], + snippets: [ + { + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines(), + linesHighlighted: randLinesHighlightedArray + } + ], tags: faker.lorem.words().split(' '), folder: folderKey } @@ -51,18 +58,20 @@ test.serial('Create a note', (t) => { input2.title = input2.content.split('\n').shift() return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return Promise.all([ createNote(storageKey, input1), createNote(storageKey, input2) ]) }) - .then(function assert (data) { + .then(function assert(data) { const data1 = data[0] const data2 = data[1] t.is(storageKey, data1.storage) - const jsonData1 = CSON.readFileSync(path.join(storagePath, 'notes', data1.key + '.cson')) + const jsonData1 = CSON.readFileSync( + path.join(storagePath, 'notes', data1.key + '.cson') + ) t.is(input1.title, data1.title) t.is(input1.title, jsonData1.title) @@ -76,11 +85,19 @@ test.serial('Create a note', (t) => { t.is(input1.snippets[0].content, jsonData1.snippets[0].content) t.is(input1.snippets[0].name, data1.snippets[0].name) t.is(input1.snippets[0].name, jsonData1.snippets[0].name) - t.deepEqual(input1.snippets[0].linesHighlighted, data1.snippets[0].linesHighlighted) - t.deepEqual(input1.snippets[0].linesHighlighted, jsonData1.snippets[0].linesHighlighted) + t.deepEqual( + input1.snippets[0].linesHighlighted, + data1.snippets[0].linesHighlighted + ) + t.deepEqual( + input1.snippets[0].linesHighlighted, + jsonData1.snippets[0].linesHighlighted + ) t.is(storageKey, data2.storage) - const jsonData2 = CSON.readFileSync(path.join(storagePath, 'notes', data2.key + '.cson')) + const jsonData2 = CSON.readFileSync( + path.join(storagePath, 'notes', data2.key + '.cson') + ) t.is(input2.title, data2.title) t.is(input2.title, jsonData2.title) t.is(input2.content, data2.content) @@ -92,7 +109,7 @@ test.serial('Create a note', (t) => { }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/createNoteFromUrl-test.js b/tests/dataApi/createNoteFromUrl-test.js index a324a3e5..83b8d4e8 100644 --- a/tests/dataApi/createNoteFromUrl-test.js +++ b/tests/dataApi/createNoteFromUrl-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -15,29 +18,32 @@ const CSON = require('@rokt33r/season') const storagePath = path.join(os.tmpdir(), 'test/create-note-from-url') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Create a note from URL', (t) => { +test.serial('Create a note from URL', t => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key const url = 'https://shapeshed.com/writing-cross-platform-node/' - return createNoteFromUrl(url, storageKey, folderKey) - .then(function assert ({ note }) { - t.is(storageKey, note.storage) - const jsonData = CSON.readFileSync(path.join(storagePath, 'notes', note.key + '.cson')) + return createNoteFromUrl(url, storageKey, folderKey).then(function assert({ + note + }) { + t.is(storageKey, note.storage) + const jsonData = CSON.readFileSync( + path.join(storagePath, 'notes', note.key + '.cson') + ) - // Test if saved content is matching the created in memory note - t.is(note.content, jsonData.content) - t.is(note.tags.length, jsonData.tags.length) - }) + // Test if saved content is matching the created in memory note + t.is(note.content, jsonData.content) + t.is(note.tags.length, jsonData.tags.length) + }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/createSnippet-test.js b/tests/dataApi/createSnippet-test.js index 638b76ca..e1b9a570 100644 --- a/tests/dataApi/createSnippet-test.js +++ b/tests/dataApi/createSnippet-test.js @@ -7,21 +7,21 @@ const path = require('path') const snippetFilePath = path.join(os.tmpdir(), 'test', 'create-snippet') const snippetFile = path.join(snippetFilePath, 'snippets.json') -test.beforeEach((t) => { +test.beforeEach(t => { sander.writeFileSync(snippetFile, '[]') }) -test.serial('Create a snippet', (t) => { +test.serial('Create a snippet', t => { return Promise.resolve() - .then(function doTest () { - return Promise.all([ - createSnippet(snippetFile) - ]) + .then(function doTest() { + return Promise.all([createSnippet(snippetFile)]) }) - .then(function assert (data) { + .then(function assert(data) { data = data[0] const snippets = JSON.parse(sander.readFileSync(snippetFile)) - const snippet = snippets.find(currentSnippet => currentSnippet.id === data.id) + const snippet = snippets.find( + currentSnippet => currentSnippet.id === data.id + ) t.not(snippet, undefined) t.is(snippet.name, data.name) t.deepEqual(snippet.prefix, data.prefix) diff --git a/tests/dataApi/deleteFolder-test.js b/tests/dataApi/deleteFolder-test.js index af901896..8b930e48 100644 --- a/tests/dataApi/deleteFolder-test.js +++ b/tests/dataApi/deleteFolder-test.js @@ -10,7 +10,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const _ = require('lodash') const TestDummy = require('../fixtures/TestDummy') @@ -20,12 +23,12 @@ const CSON = require('@rokt33r/season') const storagePath = path.join(os.tmpdir(), 'test/delete-folder') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Delete a folder', (t) => { +test.serial('Delete a folder', t => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key let noteKey @@ -33,44 +36,64 @@ test.serial('Delete a folder', (t) => { const input1 = { type: 'SNIPPET_NOTE', description: faker.lorem.lines(), - snippets: [{ - name: faker.system.fileName(), - mode: 'text', - content: faker.lorem.lines() - }], + snippets: [ + { + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines() + } + ], tags: faker.lorem.words().split(' '), folder: folderKey } input1.title = input1.description.split('\n').shift() return Promise.resolve() - .then(function prepare () { - return createNote(storageKey, input1) - .then(function createAttachmentFolder (data) { - fs.mkdirSync(path.join(storagePath, attachmentManagement.DESTINATION_FOLDER)) - fs.mkdirSync(path.join(storagePath, attachmentManagement.DESTINATION_FOLDER, data.key)) + .then(function prepare() { + return createNote(storageKey, input1).then( + function createAttachmentFolder(data) { + fs.mkdirSync( + path.join(storagePath, attachmentManagement.DESTINATION_FOLDER) + ) + fs.mkdirSync( + path.join( + storagePath, + attachmentManagement.DESTINATION_FOLDER, + data.key + ) + ) noteKey = data.key return data - }) + } + ) }) - .then(function doTest () { + .then(function doTest() { return deleteFolder(storageKey, folderKey) }) - .then(function assert (data) { - t.true(_.find(data.storage.folders, {key: folderKey}) == null) - const jsonData = CSON.readFileSync(path.join(data.storage.path, 'boostnote.json')) + .then(function assert(data) { + t.true(_.find(data.storage.folders, { key: folderKey }) == null) + const jsonData = CSON.readFileSync( + path.join(data.storage.path, 'boostnote.json') + ) - t.true(_.find(jsonData.folders, {key: folderKey}) == null) + t.true(_.find(jsonData.folders, { key: folderKey }) == null) const notePaths = sander.readdirSync(data.storage.path, 'notes') - t.is(notePaths.length, t.context.storage.notes.filter((note) => note.folder !== folderKey).length) + t.is( + notePaths.length, + t.context.storage.notes.filter(note => note.folder !== folderKey).length + ) - const attachmentFolderPath = path.join(storagePath, attachmentManagement.DESTINATION_FOLDER, noteKey) + const attachmentFolderPath = path.join( + storagePath, + attachmentManagement.DESTINATION_FOLDER, + noteKey + ) t.false(fs.existsSync(attachmentFolderPath)) }) }) -test.after.always(function after () { +test.after.always(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/deleteNote-test.js b/tests/dataApi/deleteNote-test.js index 9c809dcf..27ea478b 100644 --- a/tests/dataApi/deleteNote-test.js +++ b/tests/dataApi/deleteNote-test.js @@ -7,7 +7,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -19,56 +22,72 @@ const attachmentManagement = require('browser/main/lib/dataApi/attachmentManagem const storagePath = path.join(os.tmpdir(), 'test/delete-note') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Delete a note', (t) => { +test.serial('Delete a note', t => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key const input1 = { type: 'SNIPPET_NOTE', description: faker.lorem.lines(), - snippets: [{ - name: faker.system.fileName(), - mode: 'text', - content: faker.lorem.lines() - }], + snippets: [ + { + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines() + } + ], tags: faker.lorem.words().split(' '), folder: folderKey } input1.title = input1.description.split('\n').shift() return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return createNote(storageKey, input1) - .then(function createAttachmentFolder (data) { - fs.mkdirSync(path.join(storagePath, attachmentManagement.DESTINATION_FOLDER)) - fs.mkdirSync(path.join(storagePath, attachmentManagement.DESTINATION_FOLDER, data.key)) + .then(function createAttachmentFolder(data) { + fs.mkdirSync( + path.join(storagePath, attachmentManagement.DESTINATION_FOLDER) + ) + fs.mkdirSync( + path.join( + storagePath, + attachmentManagement.DESTINATION_FOLDER, + data.key + ) + ) return data }) - .then(function (data) { + .then(function(data) { return deleteNote(storageKey, data.key) }) }) - .then(function assert (data) { + .then(function assert(data) { try { - CSON.readFileSync(path.join(storagePath, 'notes', data.noteKey + '.cson')) + CSON.readFileSync( + path.join(storagePath, 'notes', data.noteKey + '.cson') + ) t.fail('note cson must be deleted.') } catch (err) { t.is(err.code, 'ENOENT') return data } }) - .then(function assertAttachmentFolderDeleted (data) { - const attachmentFolderPath = path.join(storagePath, attachmentManagement.DESTINATION_FOLDER, data.noteKey) + .then(function assertAttachmentFolderDeleted(data) { + const attachmentFolderPath = path.join( + storagePath, + attachmentManagement.DESTINATION_FOLDER, + data.noteKey + ) t.is(fs.existsSync(attachmentFolderPath), false) }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/deleteSnippet-test.js b/tests/dataApi/deleteSnippet-test.js index 9d13249b..52ec2875 100644 --- a/tests/dataApi/deleteSnippet-test.js +++ b/tests/dataApi/deleteSnippet-test.js @@ -14,18 +14,16 @@ const newSnippet = { content: '' } -test.beforeEach((t) => { +test.beforeEach(t => { sander.writeFileSync(snippetFile, JSON.stringify([newSnippet])) }) -test.serial('Delete a snippet', (t) => { +test.serial('Delete a snippet', t => { return Promise.resolve() - .then(function doTest () { - return Promise.all([ - deleteSnippet(newSnippet, snippetFile) - ]) + .then(function doTest() { + return Promise.all([deleteSnippet(newSnippet, snippetFile)]) }) - .then(function assert (data) { + .then(function assert(data) { data = data[0] const snippets = JSON.parse(sander.readFileSync(snippetFile)) t.is(snippets.length, 0) diff --git a/tests/dataApi/exportFolder-test.js b/tests/dataApi/exportFolder-test.js index fb4aaa7b..d0aef186 100644 --- a/tests/dataApi/exportFolder-test.js +++ b/tests/dataApi/exportFolder-test.js @@ -7,7 +7,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const os = require('os') @@ -17,12 +20,12 @@ const sander = require('sander') const storagePath = path.join(os.tmpdir(), 'test/export-note') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Export a folder', (t) => { +test.serial('Export a folder', t => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key @@ -37,24 +40,26 @@ test.serial('Export a folder', (t) => { const input2 = { type: 'SNIPPET_NOTE', description: 'Some normal text', - snippets: [{ - name: faker.system.fileName(), - mode: 'text', - content: faker.lorem.lines() - }], + snippets: [ + { + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines() + } + ], tags: faker.lorem.words().split(' '), folder: folderKey } input2.title = 'input2' return createNote(storageKey, input1) - .then(function () { + .then(function() { return createNote(storageKey, input2) }) - .then(function () { + .then(function() { return exportFolder(storageKey, folderKey, 'md', storagePath) }) - .then(function assert () { + .then(function assert() { let filePath = path.join(storagePath, 'input1.md') t.true(fs.existsSync(filePath)) filePath = path.join(storagePath, 'input2.md') @@ -62,7 +67,7 @@ test.serial('Export a folder', (t) => { }) }) -test.after.always(function after () { +test.after.always(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/exportStorage-test.js b/tests/dataApi/exportStorage-test.js index e5594329..1ee26f19 100644 --- a/tests/dataApi/exportStorage-test.js +++ b/tests/dataApi/exportStorage-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const os = require('os') @@ -17,7 +20,9 @@ test.beforeEach(t => { t.context.storageDir = path.join(os.tmpdir(), 'test/export-storage') t.context.storage = TestDummy.dummyStorage(t.context.storageDir) t.context.exportDir = path.join(os.tmpdir(), 'test/export-storage-output') - try { fs.mkdirSync(t.context.exportDir) } catch (e) {} + try { + fs.mkdirSync(t.context.exportDir) + } catch (e) {} localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) @@ -26,23 +31,25 @@ test.serial('Export a storage', t => { const folders = t.context.storage.json.folders const notes = t.context.storage.notes const exportDir = t.context.exportDir - const folderKeyToName = folders.reduce( - (acc, folder) => { - acc[folder.key] = folder.name - return acc - }, {}) - return exportStorage(storageKey, 'md', exportDir) - .then(() => { - notes.forEach(note => { - const noteDir = path.join(exportDir, folderKeyToName[note.folder], `${note.title}.md`) - if (note.type === 'MARKDOWN_NOTE') { - t.true(fs.existsSync(noteDir)) - t.is(fs.readFileSync(noteDir, 'utf8'), note.content) - } else if (note.type === 'SNIPPET_NOTE') { - t.false(fs.existsSync(noteDir)) - } - }) + const folderKeyToName = folders.reduce((acc, folder) => { + acc[folder.key] = folder.name + return acc + }, {}) + return exportStorage(storageKey, 'md', exportDir).then(() => { + notes.forEach(note => { + const noteDir = path.join( + exportDir, + folderKeyToName[note.folder], + `${note.title}.md` + ) + if (note.type === 'MARKDOWN_NOTE') { + t.true(fs.existsSync(noteDir)) + t.is(fs.readFileSync(noteDir, 'utf8'), note.content) + } else if (note.type === 'SNIPPET_NOTE') { + t.false(fs.existsSync(noteDir)) + } }) + }) }) test.afterEach.always(t => { diff --git a/tests/dataApi/init.js b/tests/dataApi/init.js index cacc2b2a..989e4969 100644 --- a/tests/dataApi/init.js +++ b/tests/dataApi/init.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const keygen = require('browser/lib/keygen') @@ -18,11 +21,16 @@ const v1StoragePath = path.join(os.tmpdir(), 'test/init-v1-storage') const legacyStoragePath = path.join(os.tmpdir(), 'test/init-legacy-storage') const emptyDirPath = path.join(os.tmpdir(), 'test/init-empty-storage') -test.beforeEach((t) => { +test.beforeEach(t => { localStorage.clear() // Prepare 3 types of dir - t.context.v1StorageData = TestDummy.dummyStorage(v1StoragePath, {cache: {name: 'v1'}}) - t.context.legacyStorageData = TestDummy.dummyLegacyStorage(legacyStoragePath, {cache: {name: 'legacy'}}) + t.context.v1StorageData = TestDummy.dummyStorage(v1StoragePath, { + cache: { name: 'v1' } + }) + t.context.legacyStorageData = TestDummy.dummyLegacyStorage( + legacyStoragePath, + { cache: { name: 'legacy' } } + ) t.context.emptyStorageData = { cache: { type: 'FILESYSTEM', @@ -32,27 +40,37 @@ test.beforeEach((t) => { } } - localStorage.setItem('storages', JSON.stringify([t.context.v1StorageData.cache, t.context.legacyStorageData.cache, t.context.emptyStorageData.cache])) + localStorage.setItem( + 'storages', + JSON.stringify([ + t.context.v1StorageData.cache, + t.context.legacyStorageData.cache, + t.context.emptyStorageData.cache + ]) + ) }) -test.serial('Initialize All Storages', (t) => { +test.serial('Initialize All Storages', t => { const { v1StorageData, legacyStorageData } = t.context return Promise.resolve() - .then(function test () { + .then(function test() { return init() }) - .then(function assert (data) { + .then(function assert(data) { t.true(Array.isArray(data.storages)) - t.is(data.notes.length, v1StorageData.notes.length + legacyStorageData.notes.length) + t.is( + data.notes.length, + v1StorageData.notes.length + legacyStorageData.notes.length + ) t.is(data.storages.length, 3) - data.storages.forEach(function assertStorage (storage) { + data.storages.forEach(function assertStorage(storage) { t.true(_.isString(storage.key)) t.true(_.isString(storage.name)) t.true(storage.type === 'FILESYSTEM') t.true(_.isString(storage.path)) }) }) - .then(function after () { + .then(function after() { localStorage.clear() }) }) diff --git a/tests/dataApi/migrateFromV6Storage-test.js b/tests/dataApi/migrateFromV6Storage-test.js index 7cdcdb71..92b87cec 100644 --- a/tests/dataApi/migrateFromV6Storage-test.js +++ b/tests/dataApi/migrateFromV6Storage-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -16,18 +19,20 @@ const os = require('os') const dummyStoragePath = path.join(os.tmpdir(), 'test/migrate-test-storage') -test.beforeEach((t) => { - const dummyData = t.context.dummyData = TestDummy.dummyLegacyStorage(dummyStoragePath) +test.beforeEach(t => { + const dummyData = (t.context.dummyData = TestDummy.dummyLegacyStorage( + dummyStoragePath + )) console.log('init count', dummyData.notes.length) localStorage.setItem('storages', JSON.stringify([dummyData.cache])) }) -test.serial('Migrate legacy storage into v1 storage', (t) => { +test.serial('Migrate legacy storage into v1 storage', t => { return Promise.resolve() - .then(function test () { + .then(function test() { return migrateFromV6Storage(dummyStoragePath) }) - .then(function assert (data) { + .then(function assert(data) { // Check the result. It must be true if succeed. t.true(data) @@ -36,29 +41,31 @@ test.serial('Migrate legacy storage into v1 storage', (t) => { const noteDirPath = path.join(dummyStoragePath, 'notes') const fileList = sander.readdirSync(noteDirPath) t.is(dummyData.notes.length, fileList.length) - const noteMap = fileList - .map((filePath) => { - return CSON.readFileSync(path.join(noteDirPath, filePath)) - }) - dummyData.notes - .forEach(function (targetNote) { - t.true(_.find(noteMap, {title: targetNote.title, folder: targetNote.folder}) != null) - }) + const noteMap = fileList.map(filePath => { + return CSON.readFileSync(path.join(noteDirPath, filePath)) + }) + dummyData.notes.forEach(function(targetNote) { + t.true( + _.find(noteMap, { + title: targetNote.title, + folder: targetNote.folder + }) != null + ) + }) // Check legacy folder directory is removed - dummyData.json.folders - .forEach(function (folder) { - try { - sander.statSync(dummyStoragePath, folder.key) - t.fail('Folder still remains. ENOENT error must be occured.') - } catch (err) { - t.is(err.code, 'ENOENT') - } - }) + dummyData.json.folders.forEach(function(folder) { + try { + sander.statSync(dummyStoragePath, folder.key) + t.fail('Folder still remains. ENOENT error must be occured.') + } catch (err) { + t.is(err.code, 'ENOENT') + } + }) }) }) -test.after.always(function () { +test.after.always(function() { localStorage.clear() sander.rimrafSync(dummyStoragePath) }) diff --git a/tests/dataApi/moveNote-test.js b/tests/dataApi/moveNote-test.js index 3fe31c58..6718af37 100644 --- a/tests/dataApi/moveNote-test.js +++ b/tests/dataApi/moveNote-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -16,13 +19,16 @@ const CSON = require('@rokt33r/season') const storagePath = path.join(os.tmpdir(), 'test/move-note') const storagePath2 = path.join(os.tmpdir(), 'test/move-note2') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage1 = TestDummy.dummyStorage(storagePath) t.context.storage2 = TestDummy.dummyStorage(storagePath2) - localStorage.setItem('storages', JSON.stringify([t.context.storage1.cache, t.context.storage2.cache])) + localStorage.setItem( + 'storages', + JSON.stringify([t.context.storage1.cache, t.context.storage2.cache]) + ) }) -test.serial('Move a note', (t) => { +test.serial('Move a note', t => { const storageKey1 = t.context.storage1.cache.key const folderKey1 = t.context.storage1.json.folders[0].key const note1 = t.context.storage1.notes[0] @@ -31,22 +37,26 @@ test.serial('Move a note', (t) => { const folderKey2 = t.context.storage2.json.folders[0].key return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return Promise.all([ moveNote(storageKey1, note1.key, storageKey1, folderKey1), moveNote(storageKey1, note2.key, storageKey2, folderKey2) ]) }) - .then(function assert (data) { + .then(function assert(data) { const data1 = data[0] const data2 = data[1] - const jsonData1 = CSON.readFileSync(path.join(storagePath, 'notes', data1.key + '.cson')) + const jsonData1 = CSON.readFileSync( + path.join(storagePath, 'notes', data1.key + '.cson') + ) t.is(jsonData1.folder, folderKey1) t.is(jsonData1.title, note1.title) - const jsonData2 = CSON.readFileSync(path.join(storagePath2, 'notes', data2.key + '.cson')) + const jsonData2 = CSON.readFileSync( + path.join(storagePath2, 'notes', data2.key + '.cson') + ) t.is(jsonData2.folder, folderKey2) t.is(jsonData2.title, note2.title) try { @@ -58,7 +68,7 @@ test.serial('Move a note', (t) => { }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) sander.rimrafSync(storagePath2) diff --git a/tests/dataApi/removeStorage-test.js b/tests/dataApi/removeStorage-test.js index 33541df1..e5ac2c8f 100644 --- a/tests/dataApi/removeStorage-test.js +++ b/tests/dataApi/removeStorage-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -14,23 +17,23 @@ const os = require('os') const storagePath = path.join(os.tmpdir(), 'test/remove-storage') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test('Remove a storage', (t) => { +test('Remove a storage', t => { const storageKey = t.context.storage.cache.key return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return removeStorage(storageKey) }) - .then(function assert (data) { + .then(function assert(data) { t.is(JSON.parse(localStorage.getItem('storages')).length, 0) }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/renameStorage-test.js b/tests/dataApi/renameStorage-test.js index f5c64cb6..46be9827 100644 --- a/tests/dataApi/renameStorage-test.js +++ b/tests/dataApi/renameStorage-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const _ = require('lodash') const TestDummy = require('../fixtures/TestDummy') @@ -15,24 +18,24 @@ const os = require('os') const storagePath = path.join(os.tmpdir(), 'test/rename-storage') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Rename a storage', (t) => { +test.serial('Rename a storage', t => { const storageKey = t.context.storage.cache.key return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return renameStorage(storageKey, 'changed') }) - .then(function assert (data) { + .then(function assert(data) { const cachedStorageList = JSON.parse(localStorage.getItem('storages')) - t.true(_.find(cachedStorageList, {key: storageKey}).name === 'changed') + t.true(_.find(cachedStorageList, { key: storageKey }).name === 'changed') }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/reorderFolder-test.js b/tests/dataApi/reorderFolder-test.js index 55bff0eb..01694b8c 100644 --- a/tests/dataApi/reorderFolder-test.js +++ b/tests/dataApi/reorderFolder-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const _ = require('lodash') const TestDummy = require('../fixtures/TestDummy') @@ -16,32 +19,34 @@ const CSON = require('@rokt33r/season') const storagePath = path.join(os.tmpdir(), 'test/reorder-folder') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Reorder a folder', (t) => { +test.serial('Reorder a folder', t => { const storageKey = t.context.storage.cache.key const firstFolderKey = t.context.storage.json.folders[0].key const secondFolderKey = t.context.storage.json.folders[1].key return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return reorderFolder(storageKey, 0, 1) }) - .then(function assert (data) { + .then(function assert(data) { t.true(_.nth(data.storage.folders, 0).key === secondFolderKey) t.true(_.nth(data.storage.folders, 1).key === firstFolderKey) - const jsonData = CSON.readFileSync(path.join(data.storage.path, 'boostnote.json')) + const jsonData = CSON.readFileSync( + path.join(data.storage.path, 'boostnote.json') + ) t.true(_.nth(jsonData.folders, 0).key === secondFolderKey) t.true(_.nth(jsonData.folders, 1).key === firstFolderKey) }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/toggleStorage-test.js b/tests/dataApi/toggleStorage-test.js index 5169a4f4..87ed0059 100644 --- a/tests/dataApi/toggleStorage-test.js +++ b/tests/dataApi/toggleStorage-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const _ = require('lodash') const TestDummy = require('../fixtures/TestDummy') @@ -15,24 +18,24 @@ const os = require('os') const storagePath = path.join(os.tmpdir(), 'test/toggle-storage') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Toggle a storage location', (t) => { +test.serial('Toggle a storage location', t => { const storageKey = t.context.storage.cache.key return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return toggleStorage(storageKey, true) }) - .then(function assert (data) { + .then(function assert(data) { const cachedStorageList = JSON.parse(localStorage.getItem('storages')) - t.true(_.find(cachedStorageList, {key: storageKey}).isOpen === true) + t.true(_.find(cachedStorageList, { key: storageKey }).isOpen === true) }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/updateFolder-test.js b/tests/dataApi/updateFolder-test.js index ca9fcf99..75ad71eb 100644 --- a/tests/dataApi/updateFolder-test.js +++ b/tests/dataApi/updateFolder-test.js @@ -6,7 +6,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const _ = require('lodash') const TestDummy = require('../fixtures/TestDummy') @@ -16,12 +19,12 @@ const CSON = require('@rokt33r/season') const storagePath = path.join(os.tmpdir(), 'test/update-folder') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Update a folder', (t) => { +test.serial('Update a folder', t => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key const input = { @@ -29,18 +32,20 @@ test.serial('Update a folder', (t) => { color: '#FF0000' } return Promise.resolve() - .then(function doTest () { + .then(function doTest() { return updateFolder(storageKey, folderKey, input) }) - .then(function assert (data) { + .then(function assert(data) { t.true(_.find(data.storage.folders, input) != null) - const jsonData = CSON.readFileSync(path.join(data.storage.path, 'boostnote.json')) + const jsonData = CSON.readFileSync( + path.join(data.storage.path, 'boostnote.json') + ) console.log(path.join(data.storage.path, 'boostnote.json')) t.true(_.find(jsonData.folders, input) != null) }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/updateNote-test.js b/tests/dataApi/updateNote-test.js index da47c30c..40e60b64 100644 --- a/tests/dataApi/updateNote-test.js +++ b/tests/dataApi/updateNote-test.js @@ -7,7 +7,10 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') @@ -17,27 +20,33 @@ const faker = require('faker') const storagePath = path.join(os.tmpdir(), 'test/update-note') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) -test.serial('Update a note', (t) => { +test.serial('Update a note', t => { const storageKey = t.context.storage.cache.key const folderKey = t.context.storage.json.folders[0].key - const randLinesHighlightedArray = new Array(10).fill().map(() => Math.round(Math.random() * 10)) - const randLinesHighlightedArray2 = new Array(15).fill().map(() => Math.round(Math.random() * 15)) + const randLinesHighlightedArray = new Array(10) + .fill() + .map(() => Math.round(Math.random() * 10)) + const randLinesHighlightedArray2 = new Array(15) + .fill() + .map(() => Math.round(Math.random() * 15)) const input1 = { type: 'SNIPPET_NOTE', description: faker.lorem.lines(), - snippets: [{ - name: faker.system.fileName(), - mode: 'text', - content: faker.lorem.lines(), - linesHighlighted: randLinesHighlightedArray - }], + snippets: [ + { + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines(), + linesHighlighted: randLinesHighlightedArray + } + ], tags: faker.lorem.words().split(' '), folder: folderKey } @@ -55,12 +64,14 @@ test.serial('Update a note', (t) => { const input3 = { type: 'SNIPPET_NOTE', description: faker.lorem.lines(), - snippets: [{ - name: faker.system.fileName(), - mode: 'text', - content: faker.lorem.lines(), - linesHighlighted: randLinesHighlightedArray2 - }], + snippets: [ + { + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines(), + linesHighlighted: randLinesHighlightedArray2 + } + ], tags: faker.lorem.words().split(' ') } input3.title = input3.description.split('\n').shift() @@ -74,26 +85,26 @@ test.serial('Update a note', (t) => { input4.title = input4.content.split('\n').shift() return Promise.resolve() - .then(function doTest () { - return Promise - .all([ - createNote(storageKey, input1), - createNote(storageKey, input2) + .then(function doTest() { + return Promise.all([ + createNote(storageKey, input1), + createNote(storageKey, input2) + ]).then(function updateNotes(data) { + const data1 = data[0] + const data2 = data[1] + return Promise.all([ + updateNote(data1.storage, data1.key, input3), + updateNote(data1.storage, data2.key, input4) ]) - .then(function updateNotes (data) { - const data1 = data[0] - const data2 = data[1] - return Promise.all([ - updateNote(data1.storage, data1.key, input3), - updateNote(data1.storage, data2.key, input4) - ]) - }) + }) }) - .then(function assert (data) { + .then(function assert(data) { const data1 = data[0] const data2 = data[1] - const jsonData1 = CSON.readFileSync(path.join(storagePath, 'notes', data1.key + '.cson')) + const jsonData1 = CSON.readFileSync( + path.join(storagePath, 'notes', data1.key + '.cson') + ) t.is(input3.title, data1.title) t.is(input3.title, jsonData1.title) t.is(input3.description, data1.description) @@ -106,10 +117,18 @@ test.serial('Update a note', (t) => { t.is(input3.snippets[0].content, jsonData1.snippets[0].content) t.is(input3.snippets[0].name, data1.snippets[0].name) t.is(input3.snippets[0].name, jsonData1.snippets[0].name) - t.deepEqual(input3.snippets[0].linesHighlighted, data1.snippets[0].linesHighlighted) - t.deepEqual(input3.snippets[0].linesHighlighted, jsonData1.snippets[0].linesHighlighted) + t.deepEqual( + input3.snippets[0].linesHighlighted, + data1.snippets[0].linesHighlighted + ) + t.deepEqual( + input3.snippets[0].linesHighlighted, + jsonData1.snippets[0].linesHighlighted + ) - const jsonData2 = CSON.readFileSync(path.join(storagePath, 'notes', data2.key + '.cson')) + const jsonData2 = CSON.readFileSync( + path.join(storagePath, 'notes', data2.key + '.cson') + ) t.is(input4.title, data2.title) t.is(input4.title, jsonData2.title) t.is(input4.content, data2.content) @@ -121,7 +140,7 @@ test.serial('Update a note', (t) => { }) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/dataApi/updateSnippet-test.js b/tests/dataApi/updateSnippet-test.js index 662548bd..5e8fb3a9 100644 --- a/tests/dataApi/updateSnippet-test.js +++ b/tests/dataApi/updateSnippet-test.js @@ -21,20 +21,20 @@ const newSnippet = { content: 'new content' } -test.beforeEach((t) => { +test.beforeEach(t => { sander.writeFileSync(snippetFile, JSON.stringify([oldSnippet])) }) -test.serial('Update a snippet', (t) => { +test.serial('Update a snippet', t => { return Promise.resolve() - .then(function doTest () { - return Promise.all([ - updateSnippet(newSnippet, snippetFile) - ]) + .then(function doTest() { + return Promise.all([updateSnippet(newSnippet, snippetFile)]) }) - .then(function assert () { + .then(function assert() { const snippets = JSON.parse(sander.readFileSync(snippetFile)) - const snippet = snippets.find(currentSnippet => currentSnippet.id === newSnippet.id) + const snippet = snippets.find( + currentSnippet => currentSnippet.id === newSnippet.id + ) t.not(snippet, undefined) t.is(snippet.name, newSnippet.name) t.deepEqual(snippet.prefix, newSnippet.prefix) diff --git a/tests/date-formatter-test.js b/tests/date-formatter-test.js index 265ca4c4..c6de28c6 100644 --- a/tests/date-formatter-test.js +++ b/tests/date-formatter-test.js @@ -5,8 +5,5 @@ const test = require('ava') const { formatDate } = require('browser/lib/date-formatter') test(t => { - t.throws( - () => formatDate('invalid argument'), - 'Invalid argument.' - ) + t.throws(() => formatDate('invalid argument'), 'Invalid argument.') }) diff --git a/tests/fixtures/TestDummy.js b/tests/fixtures/TestDummy.js index 62cae846..0c0a95d5 100644 --- a/tests/fixtures/TestDummy.js +++ b/tests/fixtures/TestDummy.js @@ -5,7 +5,7 @@ const sander = require('sander') const CSON = require('@rokt33r/season') const path = require('path') -function dummyFolder (override = {}) { +function dummyFolder(override = {}) { var data = { name: faker.lorem.word(), color: faker.internet.color() @@ -17,21 +17,23 @@ function dummyFolder (override = {}) { return data } -function dummyBoostnoteJSONData (override = {}, isLegacy = false) { +function dummyBoostnoteJSONData(override = {}, isLegacy = false) { var data = {} if (override.folders == null) { data.folders = [] - var folderCount = Math.floor((Math.random() * 5)) + 2 + var folderCount = Math.floor(Math.random() * 5) + 2 for (var i = 0; i < folderCount; i++) { var key = keygen() - while (data.folders.some((folder) => folder.key === key)) { + while (data.folders.some(folder => folder.key === key)) { key = keygen() } - data.folders.push(dummyFolder({ - key - })) + data.folders.push( + dummyFolder({ + key + }) + ) } } if (!isLegacy) data.version = '1.0' @@ -41,24 +43,28 @@ function dummyBoostnoteJSONData (override = {}, isLegacy = false) { return data } -function dummyNote (override = {}) { - var data = Math.random() > 0.5 - ? { - type: 'MARKDOWN_NOTE', - content: faker.lorem.lines() - } - : { - type: 'SNIPPET_NOTE', - description: faker.lorem.lines(), - snippets: [{ - name: faker.system.fileName(), - mode: 'text', - content: faker.lorem.lines() - }] - } - data.title = data.type === 'MARKDOWN_NOTE' - ? data.content.split('\n').shift() - : data.description.split('\n').shift() +function dummyNote(override = {}) { + var data = + Math.random() > 0.5 + ? { + type: 'MARKDOWN_NOTE', + content: faker.lorem.lines() + } + : { + type: 'SNIPPET_NOTE', + description: faker.lorem.lines(), + snippets: [ + { + name: faker.system.fileName(), + mode: 'text', + content: faker.lorem.lines() + } + ] + } + data.title = + data.type === 'MARKDOWN_NOTE' + ? data.content.split('\n').shift() + : data.description.split('\n').shift() data.createdAt = faker.date.past() data.updatedAt = faker.date.recent() data.isStarred = false @@ -91,36 +97,41 @@ function dummyNote (override = {}) { * ``` * @return {[type]} */ -function dummyStorage (storagePath, override = {}) { - var jsonData = override.json != null - ? override.json - : dummyBoostnoteJSONData() - var cacheData = override.cache != null - ? override.cache - : {} +function dummyStorage(storagePath, override = {}) { + var jsonData = + override.json != null ? override.json : dummyBoostnoteJSONData() + var cacheData = override.cache != null ? override.cache : {} if (cacheData.key == null) cacheData.key = keygen() if (cacheData.name == null) cacheData.name = faker.random.word() if (cacheData.type == null) cacheData.type = 'FILESYSTEM' cacheData.path = storagePath - sander.writeFileSync(path.join(storagePath, 'boostnote.json'), JSON.stringify(jsonData)) + sander.writeFileSync( + path.join(storagePath, 'boostnote.json'), + JSON.stringify(jsonData) + ) var notesData = [] - var noteCount = Math.floor((Math.random() * 15)) + 2 + var noteCount = Math.floor(Math.random() * 15) + 2 for (var i = 0; i < noteCount; i++) { var key = keygen(true) - while (notesData.some((note) => note.key === key)) { + while (notesData.some(note => note.key === key)) { key = keygen(true) } var noteData = dummyNote({ key, - folder: jsonData.folders[Math.floor(Math.random() * jsonData.folders.length)].key + folder: + jsonData.folders[Math.floor(Math.random() * jsonData.folders.length)] + .key }) notesData.push(noteData) } - notesData.forEach(function saveNoteCSON (note) { - CSON.writeFileSync(path.join(storagePath, 'notes', note.key + '.cson'), _.omit(note, ['key'])) + notesData.forEach(function saveNoteCSON(note) { + CSON.writeFileSync( + path.join(storagePath, 'notes', note.key + '.cson'), + _.omit(note, ['key']) + ) }) return { @@ -130,27 +141,27 @@ function dummyStorage (storagePath, override = {}) { } } -function dummyLegacyStorage (storagePath, override = {}) { - var jsonData = override.json != null - ? override.json - : dummyBoostnoteJSONData({}, true) - var cacheData = override.cache != null - ? override.cache - : {} +function dummyLegacyStorage(storagePath, override = {}) { + var jsonData = + override.json != null ? override.json : dummyBoostnoteJSONData({}, true) + var cacheData = override.cache != null ? override.cache : {} if (cacheData.key == null) cacheData.key = keygen() if (cacheData.name == null) cacheData.name = faker.random.word() if (cacheData.type == null) cacheData.type = 'FILESYSTEM' cacheData.path = storagePath - sander.writeFileSync(path.join(storagePath, 'boostnote.json'), JSON.stringify(jsonData)) + sander.writeFileSync( + path.join(storagePath, 'boostnote.json'), + JSON.stringify(jsonData) + ) var notesData = [] for (var j = 0; j < jsonData.folders.length; j++) { var folderNotes = [] - var noteCount = Math.floor((Math.random() * 5)) + 1 + var noteCount = Math.floor(Math.random() * 5) + 1 for (var i = 0; i < noteCount; i++) { var key = keygen(true) - while (folderNotes.some((note) => note.key === key)) { + while (folderNotes.some(note => note.key === key)) { key = keygen(true) } @@ -161,7 +172,10 @@ function dummyLegacyStorage (storagePath, override = {}) { folderNotes.push(noteData) } notesData = notesData.concat(folderNotes) - CSON.writeFileSync(path.join(storagePath, jsonData.folders[j].key, 'data.json'), {notes: folderNotes.map((note) => _.omit(note, ['folder']))}) + CSON.writeFileSync( + path.join(storagePath, jsonData.folders[j].key, 'data.json'), + { notes: folderNotes.map(note => _.omit(note, ['folder'])) } + ) } return { diff --git a/tests/helpers/setup-browser-env.js b/tests/helpers/setup-browser-env.js index 3e3232b7..81237b82 100644 --- a/tests/helpers/setup-browser-env.js +++ b/tests/helpers/setup-browser-env.js @@ -2,14 +2,14 @@ import browserEnv from 'browser-env' browserEnv(['window', 'document', 'navigator']) // for CodeMirror mockup -document.body.createTextRange = function () { +document.body.createTextRange = function() { return { - setEnd: function () {}, - setStart: function () {}, - getBoundingClientRect: function () { - return {right: 0} + setEnd: function() {}, + setStart: function() {}, + getBoundingClientRect: function() { + return { right: 0 } }, - getClientRects: function () { + getClientRects: function() { return { length: 0, left: 0, @@ -21,7 +21,7 @@ document.body.createTextRange = function () { window.localStorage = { // polyfill - getItem () { + getItem() { return '{}' } } diff --git a/tests/jest.js b/tests/jest.js index 6f830c67..9e59fab6 100644 --- a/tests/jest.js +++ b/tests/jest.js @@ -2,7 +2,7 @@ global.Raphael = { setWindow: jest.fn(), registerFont: jest.fn(), - fn: function () { + fn: function() { return {} } } diff --git a/tests/lib/contextMenuBuilder.test.js b/tests/lib/contextMenuBuilder.test.js index b7009bf1..689fb5f4 100644 --- a/tests/lib/contextMenuBuilder.test.js +++ b/tests/lib/contextMenuBuilder.test.js @@ -1,18 +1,32 @@ let menuBuilderParameter jest.mock('electron', () => { - return {remote: {require: jest.fn(() => { return {Menu: {buildFromTemplate: jest.fn((param) => { menuBuilderParameter = param })}} })}} + return { + remote: { + require: jest.fn(() => { + return { + Menu: { + buildFromTemplate: jest.fn(param => { + menuBuilderParameter = param + }) + } + } + }) + } + } }) const spellcheck = require('browser/lib/spellcheck') -const buildEditorContextMenu = require('browser/lib/contextMenuBuilder').buildEditorContextMenu -const buildMarkdownPreviewContextMenu = require('browser/lib/contextMenuBuilder').buildMarkdownPreviewContextMenu +const buildEditorContextMenu = require('browser/lib/contextMenuBuilder') + .buildEditorContextMenu +const buildMarkdownPreviewContextMenu = require('browser/lib/contextMenuBuilder') + .buildMarkdownPreviewContextMenu beforeEach(() => { menuBuilderParameter = null }) // Editor Context Menu -it('should make sure that no context menu is build if the passed editor instance was null', function () { +it('should make sure that no context menu is build if the passed editor instance was null', function() { const event = { pageX: 12, pageY: 12 @@ -21,89 +35,96 @@ it('should make sure that no context menu is build if the passed editor instance expect(menuBuilderParameter).toEqual(null) }) -it('should make sure that word suggestions are only requested if the word contained a typo', function () { +it('should make sure that word suggestions are only requested if the word contained a typo', function() { spellcheck.getSpellingSuggestion = jest.fn() const editor = jest.fn() editor.coordsChar = jest.fn() - editor.findWordAt = jest.fn(() => { return {anchor: {}, head: {}} }) + editor.findWordAt = jest.fn(() => { + return { anchor: {}, head: {} } + }) editor.getRange = jest.fn() editor.findMarks = jest.fn(() => []) const event = { pageX: 12, pageY: 12 } - const expectedMenuParameter = [ { role: 'cut' }, + const expectedMenuParameter = [ + { role: 'cut' }, { role: 'copy' }, { role: 'paste' }, - { role: 'selectall' } ] + { role: 'selectall' } + ] buildEditorContextMenu(editor, event) expect(menuBuilderParameter).toEqual(expectedMenuParameter) expect(spellcheck.getSpellingSuggestion).not.toHaveBeenCalled() }) -it('should make sure that word suggestions are only requested if the word contained a typo and no other mark', function () { +it('should make sure that word suggestions are only requested if the word contained a typo and no other mark', function() { spellcheck.getSpellingSuggestion = jest.fn() spellcheck.getCSSClassName = jest.fn(() => 'dummyErrorClassName') const editor = jest.fn() editor.coordsChar = jest.fn() - editor.findWordAt = jest.fn(() => { return {anchor: {}, head: {}} }) + editor.findWordAt = jest.fn(() => { + return { anchor: {}, head: {} } + }) editor.getRange = jest.fn() - const dummyMarks = [ - {className: 'someStupidClassName'} - ] + const dummyMarks = [{ className: 'someStupidClassName' }] editor.findMarks = jest.fn(() => dummyMarks) const event = { pageX: 12, pageY: 12 } - const expectedMenuParameter = [ { role: 'cut' }, + const expectedMenuParameter = [ + { role: 'cut' }, { role: 'copy' }, { role: 'paste' }, - { role: 'selectall' } ] + { role: 'selectall' } + ] buildEditorContextMenu(editor, event) expect(menuBuilderParameter).toEqual(expectedMenuParameter) expect(spellcheck.getSpellingSuggestion).not.toHaveBeenCalled() }) -it('should make sure that word suggestions calls the right editor functions', function () { +it('should make sure that word suggestions calls the right editor functions', function() { spellcheck.getSpellingSuggestion = jest.fn() spellcheck.getCSSClassName = jest.fn(() => 'dummyErrorClassName') - const dummyCursor = {dummy: 'dummy'} - const dummyRange = {anchor: {test: 'test'}, head: {test2: 'test2'}} + const dummyCursor = { dummy: 'dummy' } + const dummyRange = { anchor: { test: 'test' }, head: { test2: 'test2' } } const editor = jest.fn() editor.coordsChar = jest.fn(() => dummyCursor) editor.findWordAt = jest.fn(() => dummyRange) editor.getRange = jest.fn() - const dummyMarks = [ - {className: 'someStupidClassName'} - ] + const dummyMarks = [{ className: 'someStupidClassName' }] editor.findMarks = jest.fn(() => dummyMarks) const event = { pageX: 12, pageY: 21 } - const expectedCoordsCharCall = {left: event.pageX, top: event.pageY} + const expectedCoordsCharCall = { left: event.pageX, top: event.pageY } buildEditorContextMenu(editor, event) expect(editor.coordsChar).toHaveBeenCalledWith(expectedCoordsCharCall) expect(editor.findWordAt).toHaveBeenCalledWith(dummyCursor) - expect(editor.getRange).toHaveBeenCalledWith(dummyRange.anchor, dummyRange.head) + expect(editor.getRange).toHaveBeenCalledWith( + dummyRange.anchor, + dummyRange.head + ) }) -it('should make sure that word suggestions creates a correct menu if there was an error', function () { +it('should make sure that word suggestions creates a correct menu if there was an error', function() { const suggestions = ['test1', 'test2', 'Pustekuchen'] const errorClassName = 'errorCSS' const wordToCorrect = 'pustekuchen' - const dummyMarks = [ - {className: errorClassName} - ] + const dummyMarks = [{ className: errorClassName }] spellcheck.getSpellingSuggestion = jest.fn(() => suggestions) spellcheck.getCSSClassName = jest.fn(() => errorClassName) const editor = jest.fn() editor.coordsChar = jest.fn() - editor.findWordAt = jest.fn(() => { return {anchor: {}, head: {}} }) + editor.findWordAt = jest.fn(() => { + return { anchor: {}, head: {} } + }) editor.getRange = jest.fn(() => wordToCorrect) editor.findMarks = jest.fn(() => []) @@ -128,7 +149,7 @@ it('should make sure that word suggestions creates a correct menu if there was a }) // Markdown Preview Context Menu -it('should make sure that no context menu is built if the Markdown Preview instance was null', function () { +it('should make sure that no context menu is built if the Markdown Preview instance was null', function() { const event = { pageX: 12, pageY: 12 diff --git a/tests/lib/find-storage-test.js b/tests/lib/find-storage-test.js index 3d7fdc63..ab3b07a6 100644 --- a/tests/lib/find-storage-test.js +++ b/tests/lib/find-storage-test.js @@ -6,14 +6,17 @@ global.window = document.defaultView global.navigator = window.navigator const Storage = require('dom-storage') -const localStorage = window.localStorage = global.localStorage = new Storage(null, { strict: true }) +const localStorage = (window.localStorage = global.localStorage = new Storage( + null, + { strict: true } +)) const path = require('path') const TestDummy = require('../fixtures/TestDummy') const sander = require('sander') const os = require('os') const storagePath = path.join(os.tmpdir(), 'test/find-storage') -test.beforeEach((t) => { +test.beforeEach(t => { t.context.storage = TestDummy.dummyStorage(storagePath) localStorage.setItem('storages', JSON.stringify([t.context.storage.cache])) }) @@ -26,7 +29,7 @@ test('findStorage() should return a correct storage path(string)', t => { t.is(findStorage(storageKey).path, storagePath) }) -test.after(function after () { +test.after(function after() { localStorage.clear() sander.rimrafSync(storagePath) }) diff --git a/tests/lib/find-title-test.js b/tests/lib/find-title-test.js index 103b8774..2498cdc0 100644 --- a/tests/lib/find-title-test.js +++ b/tests/lib/find-title-test.js @@ -20,7 +20,11 @@ test('findNoteTitle#find should return a correct title (string)', t => { testCases.forEach(testCase => { const [input, expected] = testCase - t.is(findNoteTitle(input, false), expected, `Test for find() input: ${input} expected: ${expected}`) + t.is( + findNoteTitle(input, false), + expected, + `Test for find() input: ${input} expected: ${expected}` + ) }) }) @@ -34,21 +38,32 @@ test('findNoteTitle#find should ignore front matter when enableFrontMatterTitle testCases.forEach(testCase => { const [input, expected] = testCase - t.is(findNoteTitle(input, false), expected, `Test for find() input: ${input} expected: ${expected}`) + t.is( + findNoteTitle(input, false), + expected, + `Test for find() input: ${input} expected: ${expected}` + ) }) }) test('findNoteTitle#find should respect front matter when enableFrontMatterTitle=true', t => { // [input, expected] const testCases = [ - ['---\nlayout: test\ntitle: hoge hoge hoge \n---\n# fuga', 'hoge hoge hoge'], + [ + '---\nlayout: test\ntitle: hoge hoge hoge \n---\n# fuga', + 'hoge hoge hoge' + ], ['---\ntitle:hoge\n---\n# fuga', 'hoge'], ['title: fuga\n# hoge', '# hoge'] ] testCases.forEach(testCase => { const [input, expected] = testCase - t.is(findNoteTitle(input, true), expected, `Test for find() input: ${input} expected: ${expected}`) + t.is( + findNoteTitle(input, true), + expected, + `Test for find() input: ${input} expected: ${expected}` + ) }) }) @@ -61,6 +76,10 @@ test('findNoteTitle#find should respect frontMatterTitleField when provided', t testCases.forEach(testCase => { const [input, expected] = testCase - t.is(findNoteTitle(input, true, 'custom'), expected, `Test for find() input: ${input} expected: ${expected}`) + t.is( + findNoteTitle(input, true, 'custom'), + expected, + `Test for find() input: ${input} expected: ${expected}` + ) }) }) diff --git a/tests/lib/get-todo-status-test.js b/tests/lib/get-todo-status-test.js index db74ec56..52c57cfe 100644 --- a/tests/lib/get-todo-status-test.js +++ b/tests/lib/get-todo-status-test.js @@ -40,8 +40,15 @@ test('getTodoStatus should return a correct hash object', t => { testCases.forEach(testCase => { const [input, expected] = testCase - t.is(getTodoStatus(input).total, expected.total, `Test for getTodoStatus() input: ${input} expected: ${expected.total}`) - t.is(getTodoStatus(input).completed, expected.completed, `Test for getTodoStatus() input: ${input} expected: ${expected.completed}`) + t.is( + getTodoStatus(input).total, + expected.total, + `Test for getTodoStatus() input: ${input} expected: ${expected.total}` + ) + t.is( + getTodoStatus(input).completed, + expected.completed, + `Test for getTodoStatus() input: ${input} expected: ${expected.completed}` + ) }) }) - diff --git a/tests/lib/html-text-helper-test.js b/tests/lib/html-text-helper-test.js index 538b8757..9e6eb083 100644 --- a/tests/lib/html-text-helper-test.js +++ b/tests/lib/html-text-helper-test.js @@ -9,16 +9,23 @@ test('htmlTextHelper#decodeEntities should return encoded text (string)', t => { // [input, expected] const testCases = [ ['<a href=', 'Boostnote'], - ['<\\\\?php\n var = 'hoge';', '<\\\\?php\n var = \'hoge\';'], + ['var test = 'test'', "var test = 'test'"], + [ + '<a href='https://boostnote.io'>Boostnote', + "Boostnote" + ], + ['<\\\\?php\n var = 'hoge';', "<\\\\?php\n var = 'hoge';"], ['&', '&'], - ['a$'', 'a\\$\''] + ['a$'', "a\\$'"] ] testCases.forEach(testCase => { const [input, expected] = testCase - t.is(htmlTextHelper.decodeEntities(input), expected, `Test for decodeEntities() input: ${input} expected: ${expected}`) + t.is( + htmlTextHelper.decodeEntities(input), + expected, + `Test for decodeEntities() input: ${input} expected: ${expected}` + ) }) }) @@ -26,29 +33,40 @@ test('htmlTextHelper#decodeEntities() should return decoded text (string)', t => // [input, expected] const testCases = [ ['Boostnote', '<a href='https://boostnote.io'>Boostnote'], - ['Boostnote", + '<a href='https://boostnote.io'>Boostnote' + ], + [" { const [input, expected] = testCase - t.is(htmlTextHelper.encodeEntities(input), expected, `Test for encodeEntities() input: ${input} expected: ${expected}`) + t.is( + htmlTextHelper.encodeEntities(input), + expected, + `Test for encodeEntities() input: ${input} expected: ${expected}` + ) }) }) // Integration test test(t => { const testCases = [ - 'var test = \'test\'', - 'Boostnote', - '' + "var test = 'test'", + "Boostnote", + "" ] testCases.forEach(testCase => { const encodedText = htmlTextHelper.encodeEntities(testCase) const decodedText = htmlTextHelper.decodeEntities(encodedText) - t.is(decodedText, testCase, 'Integration test through encodedText() and decodedText()') + t.is( + decodedText, + testCase, + 'Integration test through encodedText() and decodedText()' + ) }) }) diff --git a/tests/lib/markdown-text-helper-test.js b/tests/lib/markdown-text-helper-test.js index 38ee3136..07d05dde 100644 --- a/tests/lib/markdown-text-helper-test.js +++ b/tests/lib/markdown-text-helper-test.js @@ -42,6 +42,10 @@ test(t => { testCases.forEach(testCase => { const [input, expected] = testCase - t.is(markdown.strip(input), expected, `Test for strip() input: ${input} expected: ${expected}`) + t.is( + markdown.strip(input), + expected, + `Test for strip() input: ${input} expected: ${expected}` + ) }) }) diff --git a/tests/lib/markdown-toc-generator-test.js b/tests/lib/markdown-toc-generator-test.js index 60568741..def2bcf8 100644 --- a/tests/lib/markdown-toc-generator-test.js +++ b/tests/lib/markdown-toc-generator-test.js @@ -261,7 +261,11 @@ this is a text const expectedToc = testCase[2].trim() const generatedToc = markdownToc.generate(inputMd) - t.is(generatedToc, expectedToc, `generate test : ${title} , generated : ${EOL}${generatedToc}, expected : ${EOL}${expectedToc}`) + t.is( + generatedToc, + expectedToc, + `generate test : ${title} , generated : ${EOL}${generatedToc}, expected : ${EOL}${expectedToc}` + ) }) }) @@ -279,7 +283,7 @@ test(t => { const testCases = [ [ `***************************** Empty note, cursor at the top`, - {line: 0, ch: 0}, + { line: 0, ch: 0 }, ``, ` @@ -291,7 +295,7 @@ test(t => { ], [ `***************************** Two level note,TOC at the beginning `, - {line: 0, ch: 0}, + { line: 0, ch: 0 }, ` # one this is a level one text @@ -329,7 +333,7 @@ this is a level one text ], [ `***************************** Two level note, cursor just after 'header text' `, - {line: 1, ch: 12}, + { line: 1, ch: 12 }, ` # header header text @@ -373,7 +377,7 @@ this is a level one text ], [ `***************************** Two level note, cursor at empty line under 'header text' `, - {line: 2, ch: 0}, + { line: 2, ch: 0 }, ` # header header text @@ -416,7 +420,7 @@ this is a level one text ], [ `***************************** Two level note, cursor just before 'text' word`, - {line: 1, ch: 8}, + { line: 1, ch: 8 }, ` # header header text @@ -461,7 +465,7 @@ this is a level one text ], [ `***************************** Already generated TOC without header file, regenerate TOC in place, no changes`, - {line: 13, ch: 0}, + { line: 13, ch: 0 }, ` # header header text @@ -511,7 +515,7 @@ this is a level one text ], [ `***************************** Already generated TOC, needs updating in place`, - {line: 0, ch: 0}, + { line: 0, ch: 0 }, ` # header header text @@ -561,7 +565,7 @@ this is a level one text ], [ `***************************** Document with cursor at the last line, expecting empty TOC `, - {line: 13, ch: 30}, + { line: 13, ch: 30 }, ` # header header text @@ -602,7 +606,7 @@ this is a level one text ], [ `***************************** Empty, not actual TOC , should be supplemented with two new points beneath`, - {line: 0, ch: 0}, + { line: 0, ch: 0 }, ` # header header text @@ -663,6 +667,10 @@ this is a level one text editor.setCursor(cursor) markdownToc.generateInEditor(editor) - t.is(expectedMd, editor.getValue(), `generateInEditor test : ${title} , generated : ${EOL}${editor.getValue()}, expected : ${EOL}${expectedMd}`) + t.is( + expectedMd, + editor.getValue(), + `generateInEditor test : ${title} , generated : ${EOL}${editor.getValue()}, expected : ${EOL}${expectedMd}` + ) }) }) diff --git a/tests/lib/normalize-editor-font-family-test.js b/tests/lib/normalize-editor-font-family-test.js index aacd03ac..beb2b765 100644 --- a/tests/lib/normalize-editor-font-family-test.js +++ b/tests/lib/normalize-editor-font-family-test.js @@ -12,5 +12,8 @@ test('normalizeEditorFontFamily() should return default font family (string[])', test('normalizeEditorFontFamily(["hoge", "huga"]) should return default font family connected with arg.', t => { const arg = 'font1, font2' - t.is(normalizeEditorFontFamily(arg), `${arg}, ${defaultEditorFontFamily.join(', ')}`) + t.is( + normalizeEditorFontFamily(arg), + `${arg}, ${defaultEditorFontFamily.join(', ')}` + ) }) diff --git a/tests/lib/rc-parser-test.js b/tests/lib/rc-parser-test.js index 024a2d36..2c8213ea 100644 --- a/tests/lib/rc-parser-test.js +++ b/tests/lib/rc-parser-test.js @@ -4,8 +4,42 @@ const { parse } = require('browser/lib/RcParser') // Unit test test('RcParser should return a json object', t => { - const validJson = { 'editor': { 'keyMap': 'vim', 'switchPreview': 'BLUR', 'theme': 'monokai' }, 'hotkey': { 'toggleMain': 'Control + L' }, 'listWidth': 135, 'navWidth': 135 } - const allJson = { 'amaEnabled': true, 'editor': { 'fontFamily': 'Monaco, Consolas', 'fontSize': '14', 'indentSize': '2', 'indentType': 'space', 'keyMap': 'vim', 'switchPreview': 'BLUR', 'theme': 'monokai' }, 'hotkey': { 'toggleMain': 'Cmd + Alt + L' }, 'isSideNavFolded': false, 'listStyle': 'DEFAULT', 'listWidth': 174, 'navWidth': 200, 'preview': { 'codeBlockTheme': 'dracula', 'fontFamily': 'Lato', 'fontSize': '14', 'lineNumber': true }, 'sortBy': { 'default': 'UPDATED_AT' }, 'ui': { 'defaultNote': 'ALWAYS_ASK', 'disableDirectWrite': false, 'theme': 'default' }, 'zoom': 1 } + const validJson = { + editor: { keyMap: 'vim', switchPreview: 'BLUR', theme: 'monokai' }, + hotkey: { toggleMain: 'Control + L' }, + listWidth: 135, + navWidth: 135 + } + const allJson = { + amaEnabled: true, + editor: { + fontFamily: 'Monaco, Consolas', + fontSize: '14', + indentSize: '2', + indentType: 'space', + keyMap: 'vim', + switchPreview: 'BLUR', + theme: 'monokai' + }, + hotkey: { toggleMain: 'Cmd + Alt + L' }, + isSideNavFolded: false, + listStyle: 'DEFAULT', + listWidth: 174, + navWidth: 200, + preview: { + codeBlockTheme: 'dracula', + fontFamily: 'Lato', + fontSize: '14', + lineNumber: true + }, + sortBy: { default: 'UPDATED_AT' }, + ui: { + defaultNote: 'ALWAYS_ASK', + disableDirectWrite: false, + theme: 'default' + }, + zoom: 1 + } // [input, expected] const validTestCases = [ @@ -13,21 +47,27 @@ test('RcParser should return a json object', t => { ['.boostnoterc.all', allJson] ] - const invalidTestCases = [ - ['.boostnoterc.invalid', {}] - ] + const invalidTestCases = [['.boostnoterc.invalid', {}]] validTestCases.forEach(validTestCase => { const [input, expected] = validTestCase - t.is(parse(filePath(input)).editor.keyMap, expected.editor.keyMap, `Test for getTodoStatus() input: ${input} expected: ${expected.keyMap}`) + t.is( + parse(filePath(input)).editor.keyMap, + expected.editor.keyMap, + `Test for getTodoStatus() input: ${input} expected: ${expected.keyMap}` + ) }) invalidTestCases.forEach(invalidTestCase => { const [input, expected] = invalidTestCase - t.is(parse(filePath(input)).editor, expected.editor, `Test for getTodoStatus() input: ${input} expected: ${expected.editor}`) + t.is( + parse(filePath(input)).editor, + expected.editor, + `Test for getTodoStatus() input: ${input} expected: ${expected.editor}` + ) }) }) -function filePath (filename) { +function filePath(filename) { return path.join(`${__dirname}/boostnoterc`, filename) } diff --git a/tests/lib/search-test.js b/tests/lib/search-test.js index 2e288d26..75180d9e 100644 --- a/tests/lib/search-test.js +++ b/tests/lib/search-test.js @@ -3,14 +3,21 @@ import searchFromNotes from 'browser/lib/search' import { dummyNote } from '../fixtures/TestDummy' import _ from 'lodash' -const pickContents = (notes) => notes.map((note) => { return note.content }) +const pickContents = notes => + notes.map(note => { + return note.content + }) let notes = [] let note1, note2, note3 test.before(t => { const data1 = { type: 'MARKDOWN_NOTE', content: 'content1', tags: ['tag1'] } - const data2 = { type: 'MARKDOWN_NOTE', content: 'content1\ncontent2', tags: ['tag1', 'tag2'] } + const data2 = { + type: 'MARKDOWN_NOTE', + content: 'content1\ncontent2', + tags: ['tag1', 'tag2'] + } const data3 = { type: 'MARKDOWN_NOTE', content: '#content4', tags: ['tag1'] } note1 = dummyNote(data1) @@ -34,12 +41,12 @@ test('it can find notes by tags and words', t => { ['#tag2 content1', [note2.content]], ['content1 #tag2', [note2.content]] ] - const testWithTagsWithoutHash = testWithTags.map(function (testCase) { + const testWithTagsWithoutHash = testWithTags.map(function(testCase) { return [testCase[0].replace(/#/g, ''), testCase[1]] }) const testCases = testWithTags.concat(testWithTagsWithoutHash) - testCases.forEach((testCase) => { + testCases.forEach(testCase => { const [input, expectedContents] = testCase const results = searchFromNotes(notes, input) t.true(_.isEqual(pickContents(results).sort(), expectedContents.sort())) diff --git a/tests/lib/slugify-test.js b/tests/lib/slugify-test.js index 0277bd10..39991bca 100644 --- a/tests/lib/slugify-test.js +++ b/tests/lib/slugify-test.js @@ -13,7 +13,7 @@ test('alphabet and digit', t => { test('should delete unavailable symbols', t => { const availableSymbols = '_-' - const testCase = availableSymbols + '][!\'#$%&()*+,./:;<=>?@\\^{|}~`' + const testCase = availableSymbols + "][!'#$%&()*+,./:;<=>?@\\^{|}~`" const decodeSlug = decodeURI(slugify(testCase)) t.true(decodeSlug === availableSymbols) diff --git a/tests/lib/snapshots/markdown-test.js.md b/tests/lib/snapshots/markdown-test.js.md index 1ebc0275..37976ef1 100644 --- a/tests/lib/snapshots/markdown-test.js.md +++ b/tests/lib/snapshots/markdown-test.js.md @@ -87,7 +87,7 @@ Generated by [AVA](https://ava.li). > Snapshot 1 - `

    ␊ + `

    • H1
      • H2␊ @@ -98,7 +98,7 @@ Generated by [AVA](https://ava.li).
    ␊ -

    ␊ +

    H1

    H2

    H3

    ␊ @@ -226,39 +226,4 @@ Generated by [AVA](https://ava.li). > Snapshot 2 `

    This is a "QUOTE".

    ␊ - - -## Markdown.render() should render PlantUML Ditaa correctly - -> Snapshot 1 - - `uml diagram␊ ` - -## Markdown.render() should render PlantUML Gantt correctly - -> Snapshot 1 - - `uml diagram␊ - ` - -## Markdown.render() should render PlantUML MindMaps correctly - -> Snapshot 1 - - `uml diagram␊ - ` - -## Markdown.render() should render PlantUML Umls correctly - -> Snapshot 1 - - `uml diagram␊ - ` - -## Markdown.render() should render PlantUML WBS correctly - -> Snapshot 1 - - `uml diagram␊ - ` \ No newline at end of file diff --git a/tests/lib/snapshots/markdown-test.js.snap b/tests/lib/snapshots/markdown-test.js.snap index 3e68ab1b..ade26489 100644 Binary files a/tests/lib/snapshots/markdown-test.js.snap and b/tests/lib/snapshots/markdown-test.js.snap differ diff --git a/tests/lib/spellcheck.test.js b/tests/lib/spellcheck.test.js index 6ebaaf3b..c3fc0b52 100644 --- a/tests/lib/spellcheck.test.js +++ b/tests/lib/spellcheck.test.js @@ -9,12 +9,12 @@ beforeEach(() => { Typo.mockClear() }) -it('should test that checkWord does not marks words that do not contain a typo', function () { +it('should test that checkWord does not marks words that do not contain a typo', function() { const testWord = 'testWord' const editor = jest.fn() editor.getRange = jest.fn(() => testWord) editor.markText = jest.fn() - const range = {anchor: {line: 1, ch: 0}, head: {line: 1, ch: 10}} + const range = { anchor: { line: 1, ch: 0 }, head: { line: 1, ch: 10 } } const mockDictionary = jest.fn() mockDictionary.check = jest.fn(() => true) systemUnderTest.setDictionaryForTestsOnly(mockDictionary) @@ -26,12 +26,12 @@ it('should test that checkWord does not marks words that do not contain a typo', expect(editor.markText).not.toHaveBeenCalled() }) -it('should test that checkWord should marks words that contain a typo', function () { +it('should test that checkWord should marks words that contain a typo', function() { const testWord = 'testWord' const editor = jest.fn() editor.getRange = jest.fn(() => testWord) editor.markText = jest.fn() - const range = {anchor: {line: 1, ch: 0}, head: {line: 1, ch: 10}} + const range = { anchor: { line: 1, ch: 0 }, head: { line: 1, ch: 10 } } const mockDictionary = jest.fn() mockDictionary.check = jest.fn(() => false) systemUnderTest.setDictionaryForTestsOnly(mockDictionary) @@ -40,14 +40,16 @@ it('should test that checkWord should marks words that contain a typo', function expect(editor.getRange).toHaveBeenCalledWith(range.anchor, range.head) expect(mockDictionary.check).toHaveBeenCalledWith(testWord) - expect(editor.markText).toHaveBeenCalledWith(range.anchor, range.head, {'className': systemUnderTest.CSS_ERROR_CLASS}) + expect(editor.markText).toHaveBeenCalledWith(range.anchor, range.head, { + className: systemUnderTest.CSS_ERROR_CLASS + }) }) -it('should test that setLanguage clears all marks', function () { +it('should test that setLanguage clears all marks', function() { const dummyMarks = [ - {clear: jest.fn()}, - {clear: jest.fn()}, - {clear: jest.fn()} + { clear: jest.fn() }, + { clear: jest.fn() }, + { clear: jest.fn() } ] const editor = jest.fn() editor.getAllMarks = jest.fn(() => dummyMarks) @@ -60,10 +62,12 @@ it('should test that setLanguage clears all marks', function () { } }) -it('should test that setLanguage with DISABLED as a lang argument should not load any dictionary and not check the whole document', function () { +it('should test that setLanguage with DISABLED as a lang argument should not load any dictionary and not check the whole document', function() { const editor = jest.fn() editor.getAllMarks = jest.fn(() => []) - const checkWholeDocumentSpy = jest.spyOn(systemUnderTest, 'checkWholeDocument').mockImplementation() + const checkWholeDocumentSpy = jest + .spyOn(systemUnderTest, 'checkWholeDocument') + .mockImplementation() systemUnderTest.setLanguage(editor, systemUnderTest.SPELLCHECK_DISABLED) @@ -72,38 +76,45 @@ it('should test that setLanguage with DISABLED as a lang argument should not loa checkWholeDocumentSpy.mockRestore() }) -it('should test that setLanguage loads the correct dictionary', function () { +it('should test that setLanguage loads the correct dictionary', function() { const editor = jest.fn() editor.getAllMarks = jest.fn(() => []) const lang = 'de_DE' - const checkWholeDocumentSpy = jest.spyOn(systemUnderTest, 'checkWholeDocument').mockImplementation() + const checkWholeDocumentSpy = jest + .spyOn(systemUnderTest, 'checkWholeDocument') + .mockImplementation() expect(Typo).not.toHaveBeenCalled() systemUnderTest.setLanguage(editor, lang) expect(Typo).toHaveBeenCalledWith(lang, false, false, expect.anything()) - expect(Typo.mock.calls[0][3].dictionaryPath).toEqual(systemUnderTest.DICTIONARY_PATH) + expect(Typo.mock.calls[0][3].dictionaryPath).toEqual( + systemUnderTest.DICTIONARY_PATH + ) expect(Typo.mock.calls[0][3].asyncLoad).toBe(true) checkWholeDocumentSpy.mockRestore() }) -it('should test that checkMultiLineRange performs checks for each word in the stated range', function () { +it('should test that checkMultiLineRange performs checks for each word in the stated range', function() { const dic = jest.fn() dic.check = jest.fn() systemUnderTest.setDictionaryForTestsOnly(dic) - document.body.createTextRange = jest.fn(() => document.createElement('textArea')) + document.body.createTextRange = jest.fn(() => + document.createElement('textArea') + ) const editor = new CodeMirror(jest.fn()) editor.setValue( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vehicula sem id tempor sollicitudin. Sed eu sagittis ligula. Maecenas sit amet velit enim. Etiam massa urna, elementum et sapien sit amet, vestibulum pharetra lectus. Nulla consequat malesuada nunc in aliquam. Vivamus faucibus orci et faucibus maximus. Pellentesque at dolor ac mi mollis molestie in facilisis nisl.\n' + - '\n' + - 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + - 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + - '\n' + - 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + - '\n' + - 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ') - const rangeFrom = {line: 2, ch: 4} - const rangeTo = {line: 3, ch: 36} + '\n' + + 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + + 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + + '\n' + + 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + + '\n' + + 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ' + ) + const rangeFrom = { line: 2, ch: 4 } + const rangeTo = { line: 3, ch: 36 } systemUnderTest.checkMultiLineRange(editor, rangeFrom, rangeTo) @@ -121,23 +132,26 @@ it('should test that checkMultiLineRange performs checks for each word in the st expect(dic.check.mock.calls[10][0]).toEqual('tristique') }) -it('should test that checkMultiLineRange works correct even when the range is inverted (from is the later position and to the lower)', function () { +it('should test that checkMultiLineRange works correct even when the range is inverted (from is the later position and to the lower)', function() { const dic = jest.fn() dic.check = jest.fn() systemUnderTest.setDictionaryForTestsOnly(dic) - document.body.createTextRange = jest.fn(() => document.createElement('textArea')) + document.body.createTextRange = jest.fn(() => + document.createElement('textArea') + ) const editor = new CodeMirror(jest.fn()) editor.setValue( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vehicula sem id tempor sollicitudin. Sed eu sagittis ligula. Maecenas sit amet velit enim. Etiam massa urna, elementum et sapien sit amet, vestibulum pharetra lectus. Nulla consequat malesuada nunc in aliquam. Vivamus faucibus orci et faucibus maximus. Pellentesque at dolor ac mi mollis molestie in facilisis nisl.\n' + - '\n' + - 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + - 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + - '\n' + - 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + - '\n' + - 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ') - const rangeFrom = {line: 3, ch: 36} - const rangeTo = {line: 2, ch: 4} + '\n' + + 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + + 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + + '\n' + + 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + + '\n' + + 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ' + ) + const rangeFrom = { line: 3, ch: 36 } + const rangeTo = { line: 2, ch: 4 } systemUnderTest.checkMultiLineRange(editor, rangeFrom, rangeTo) @@ -155,23 +169,26 @@ it('should test that checkMultiLineRange works correct even when the range is in expect(dic.check.mock.calls[10][0]).toEqual('tristique') }) -it('should test that checkMultiLineRange works for single line', function () { +it('should test that checkMultiLineRange works for single line', function() { const dic = jest.fn() dic.check = jest.fn() systemUnderTest.setDictionaryForTestsOnly(dic) - document.body.createTextRange = jest.fn(() => document.createElement('textArea')) + document.body.createTextRange = jest.fn(() => + document.createElement('textArea') + ) const editor = new CodeMirror(jest.fn()) editor.setValue( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vehicula sem id tempor sollicitudin. Sed eu sagittis ligula. Maecenas sit amet velit enim. Etiam massa urna, elementum et sapien sit amet, vestibulum pharetra lectus. Nulla consequat malesuada nunc in aliquam. Vivamus faucibus orci et faucibus maximus. Pellentesque at dolor ac mi mollis molestie in facilisis nisl.\n' + - '\n' + - 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + - 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + - '\n' + - 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + - '\n' + - 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ') - const rangeFrom = {line: 5, ch: 14} - const rangeTo = {line: 5, ch: 39} + '\n' + + 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + + 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + + '\n' + + 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + + '\n' + + 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ' + ) + const rangeFrom = { line: 5, ch: 14 } + const rangeTo = { line: 5, ch: 39 } systemUnderTest.checkMultiLineRange(editor, rangeFrom, rangeTo) @@ -181,23 +198,26 @@ it('should test that checkMultiLineRange works for single line', function () { expect(dic.check.mock.calls[2][0]).toEqual('ullamcorper') }) -it('should test that checkMultiLineRange works for single word', function () { +it('should test that checkMultiLineRange works for single word', function() { const dic = jest.fn() dic.check = jest.fn() systemUnderTest.setDictionaryForTestsOnly(dic) - document.body.createTextRange = jest.fn(() => document.createElement('textArea')) + document.body.createTextRange = jest.fn(() => + document.createElement('textArea') + ) const editor = new CodeMirror(jest.fn()) editor.setValue( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vehicula sem id tempor sollicitudin. Sed eu sagittis ligula. Maecenas sit amet velit enim. Etiam massa urna, elementum et sapien sit amet, vestibulum pharetra lectus. Nulla consequat malesuada nunc in aliquam. Vivamus faucibus orci et faucibus maximus. Pellentesque at dolor ac mi mollis molestie in facilisis nisl.\n' + - '\n' + - 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + - 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + - '\n' + - 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + - '\n' + - 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ') - const rangeFrom = {line: 7, ch: 6} - const rangeTo = {line: 7, ch: 6} + '\n' + + 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + + 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + + '\n' + + 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + + '\n' + + 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ' + ) + const rangeFrom = { line: 7, ch: 6 } + const rangeTo = { line: 7, ch: 6 } systemUnderTest.checkMultiLineRange(editor, rangeFrom, rangeTo) @@ -205,8 +225,10 @@ it('should test that checkMultiLineRange works for single word', function () { expect(dic.check.mock.calls[0][0]).toEqual('molestie') }) -it('should make sure that liveSpellcheck don\'t work if the spellcheck is not enabled', function () { - const checkMultiLineRangeSpy = jest.spyOn(systemUnderTest, 'checkMultiLineRange').mockImplementation() +it("should make sure that liveSpellcheck don't work if the spellcheck is not enabled", function() { + const checkMultiLineRangeSpy = jest + .spyOn(systemUnderTest, 'checkMultiLineRange') + .mockImplementation() const editor = jest.fn() editor.findMarks = jest.fn() @@ -219,61 +241,88 @@ it('should make sure that liveSpellcheck don\'t work if the spellcheck is not en checkMultiLineRangeSpy.mockRestore() }) -it('should make sure that liveSpellcheck works for a range of changes', function () { +it('should make sure that liveSpellcheck works for a range of changes', function() { const editor = jest.fn() - const marks = [{clear: jest.fn()}, {clear: jest.fn()}] + const marks = [{ clear: jest.fn() }, { clear: jest.fn() }] editor.findMarks = jest.fn(() => marks) - const checkMultiLineRangeSpy = jest.spyOn(systemUnderTest, 'checkMultiLineRange').mockImplementation() + const checkMultiLineRangeSpy = jest + .spyOn(systemUnderTest, 'checkMultiLineRange') + .mockImplementation() - const inputChangeRange = {from: {line: 0, ch: 2}, to: {line: 1, ch: 1}} - const inputChangeRangeTo = {from: {line: 0, ch: 2}, to: {line: 1, ch: 2}} + const inputChangeRange = { from: { line: 0, ch: 2 }, to: { line: 1, ch: 1 } } + const inputChangeRangeTo = { + from: { line: 0, ch: 2 }, + to: { line: 1, ch: 2 } + } systemUnderTest.setDictionaryForTestsOnly({}) systemUnderTest.checkChangeRange(editor, inputChangeRange, inputChangeRangeTo) - expect(checkMultiLineRangeSpy).toHaveBeenCalledWith(editor, {line: 0, ch: 1}, {line: 1, ch: 3}) - expect(editor.findMarks).toHaveBeenCalledWith({line: 0, ch: 1}, {line: 1, ch: 3}) + expect(checkMultiLineRangeSpy).toHaveBeenCalledWith( + editor, + { line: 0, ch: 1 }, + { line: 1, ch: 3 } + ) + expect(editor.findMarks).toHaveBeenCalledWith( + { line: 0, ch: 1 }, + { line: 1, ch: 3 } + ) expect(marks[0].clear).toHaveBeenCalled() expect(marks[1].clear).toHaveBeenCalled() checkMultiLineRangeSpy.mockRestore() }) -it('should make sure that liveSpellcheck works if ranges are inverted', function () { +it('should make sure that liveSpellcheck works if ranges are inverted', function() { const editor = jest.fn() - const marks = [{clear: jest.fn()}, {clear: jest.fn()}] + const marks = [{ clear: jest.fn() }, { clear: jest.fn() }] editor.findMarks = jest.fn(() => marks) - const checkMultiLineRangeSpy = jest.spyOn(systemUnderTest, 'checkMultiLineRange').mockImplementation() + const checkMultiLineRangeSpy = jest + .spyOn(systemUnderTest, 'checkMultiLineRange') + .mockImplementation() - const inputChangeRange = {from: {line: 0, ch: 2}, to: {line: 1, ch: 2}} - const inputChangeRangeTo = {from: {line: 0, ch: 2}, to: {line: 1, ch: 1}} + const inputChangeRange = { from: { line: 0, ch: 2 }, to: { line: 1, ch: 2 } } + const inputChangeRangeTo = { + from: { line: 0, ch: 2 }, + to: { line: 1, ch: 1 } + } systemUnderTest.setDictionaryForTestsOnly({}) systemUnderTest.checkChangeRange(editor, inputChangeRange, inputChangeRangeTo) - expect(checkMultiLineRangeSpy).toHaveBeenCalledWith(editor, {line: 0, ch: 1}, {line: 1, ch: 3}) - expect(editor.findMarks).toHaveBeenCalledWith({line: 0, ch: 1}, {line: 1, ch: 3}) + expect(checkMultiLineRangeSpy).toHaveBeenCalledWith( + editor, + { line: 0, ch: 1 }, + { line: 1, ch: 3 } + ) + expect(editor.findMarks).toHaveBeenCalledWith( + { line: 0, ch: 1 }, + { line: 1, ch: 3 } + ) expect(marks[0].clear).toHaveBeenCalled() expect(marks[1].clear).toHaveBeenCalled() checkMultiLineRangeSpy.mockRestore() }) -it('should make sure that liveSpellcheck works for a single word with change at the beginning', function () { +it('should make sure that liveSpellcheck works for a single word with change at the beginning', function() { const dic = jest.fn() dic.check = jest.fn() systemUnderTest.setDictionaryForTestsOnly(dic) - document.body.createTextRange = jest.fn(() => document.createElement('textArea')) + document.body.createTextRange = jest.fn(() => + document.createElement('textArea') + ) const editor = new CodeMirror(jest.fn()) editor.setValue( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vehicula sem id tempor sollicitudin. Sed eu sagittis ligula. Maecenas sit amet velit enim. Etiam massa urna, elementum et sapien sit amet, vestibulum pharetra lectus. Nulla consequat malesuada nunc in aliquam. Vivamus faucibus orci et faucibus maximus. Pellentesque at dolor ac mi mollis molestie in facilisis nisl.\n' + - '\n' + - 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + - 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + - '\n' + - 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + - '\n' + - 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ') - const rangeFrom = {from: {line: 7, ch: 6}, to: {line: 7, ch: 6}} - const rangeTo = {from: {line: 7, ch: 6}, to: {line: 7, ch: 6}} + '\n' + + 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + + 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + + '\n' + + 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + + '\n' + + 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ' + ) + const rangeFrom = { from: { line: 7, ch: 6 }, to: { line: 7, ch: 6 } } + const rangeTo = { from: { line: 7, ch: 6 }, to: { line: 7, ch: 6 } } systemUnderTest.checkChangeRange(editor, rangeFrom, rangeTo) @@ -281,23 +330,26 @@ it('should make sure that liveSpellcheck works for a single word with change at expect(dic.check.mock.calls[0][0]).toEqual('molestie') }) -it('should make sure that liveSpellcheck works for a single word with change in the middle', function () { +it('should make sure that liveSpellcheck works for a single word with change in the middle', function() { const dic = jest.fn() dic.check = jest.fn() systemUnderTest.setDictionaryForTestsOnly(dic) - document.body.createTextRange = jest.fn(() => document.createElement('textArea')) + document.body.createTextRange = jest.fn(() => + document.createElement('textArea') + ) const editor = new CodeMirror(jest.fn()) editor.setValue( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vehicula sem id tempor sollicitudin. Sed eu sagittis ligula. Maecenas sit amet velit enim. Etiam massa urna, elementum et sapien sit amet, vestibulum pharetra lectus. Nulla consequat malesuada nunc in aliquam. Vivamus faucibus orci et faucibus maximus. Pellentesque at dolor ac mi mollis molestie in facilisis nisl.\n' + - '\n' + - 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + - 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + - '\n' + - 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + - '\n' + - 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ') - const rangeFrom = {from: {line: 7, ch: 9}, to: {line: 7, ch: 9}} - const rangeTo = {from: {line: 7, ch: 9}, to: {line: 7, ch: 9}} + '\n' + + 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + + 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + + '\n' + + 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + + '\n' + + 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ' + ) + const rangeFrom = { from: { line: 7, ch: 9 }, to: { line: 7, ch: 9 } } + const rangeTo = { from: { line: 7, ch: 9 }, to: { line: 7, ch: 9 } } systemUnderTest.checkChangeRange(editor, rangeFrom, rangeTo) @@ -305,23 +357,26 @@ it('should make sure that liveSpellcheck works for a single word with change in expect(dic.check.mock.calls[0][0]).toEqual('molestie') }) -it('should make sure that liveSpellcheck works for a single word with change at the end', function () { +it('should make sure that liveSpellcheck works for a single word with change at the end', function() { const dic = jest.fn() dic.check = jest.fn() systemUnderTest.setDictionaryForTestsOnly(dic) - document.body.createTextRange = jest.fn(() => document.createElement('textArea')) + document.body.createTextRange = jest.fn(() => + document.createElement('textArea') + ) const editor = new CodeMirror(jest.fn()) editor.setValue( 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vivamus vehicula sem id tempor sollicitudin. Sed eu sagittis ligula. Maecenas sit amet velit enim. Etiam massa urna, elementum et sapien sit amet, vestibulum pharetra lectus. Nulla consequat malesuada nunc in aliquam. Vivamus faucibus orci et faucibus maximus. Pellentesque at dolor ac mi mollis molestie in facilisis nisl.\n' + - '\n' + - 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + - 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + - '\n' + - 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + - '\n' + - 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ') - const rangeFrom = {from: {line: 7, ch: 14}, to: {line: 7, ch: 14}} - const rangeTo = {from: {line: 7, ch: 14}, to: {line: 7, ch: 14}} + '\n' + + 'Nam id lacus et elit sollicitudin vestibulum. Phasellus blandit laoreet odio \n' + + 'Ut tristique risus et mi tristique, in aliquam odio laoreet. Curabitur nunc felis, mollis ut laoreet quis, finibus in nibh. Proin urna risus, rhoncus at diam interdum, maximus vestibulum nulla. Maecenas ut rutrum nulla, vel finibus est. Etiam placerat mi et libero volutpat, tristique rhoncus felis volutpat. Donec quam erat, congue quis ligula eget, mollis aliquet elit. Vestibulum feugiat odio sit amet ex dignissim, sit amet vulputate lectus iaculis. Sed tempus id enim at eleifend. Nullam bibendum eleifend congue. Pellentesque varius arcu elit, at accumsan dolor ultrices vitae. Etiam condimentum lectus id dolor fringilla tempor. Aliquam nec fringilla sem. Fusce ac quam porta, molestie nunc sed, semper nisl. Curabitur luctus sem in dapibus gravida. Suspendisse scelerisque mollis rutrum. Proin lacinia dolor sit amet ornare condimentum.\n' + + '\n' + + 'In ex neque, volutpat quis ullamcorper in, vestibulum vel ligula. Quisque lobortis eget neque quis ullamcorper. Nunc purus lorem, scelerisque in malesuada id, congue a magna. Donec rutrum maximus egestas. Nulla ornare libero quis odio ultricies iaculis. Suspendisse consectetur bibendum purus ac blandit. Donec et neque quis dolor eleifend tempus. Fusce fringilla risus id venenatis rutrum. Mauris commodo posuere ipsum, sit amet hendrerit risus lacinia quis. Aenean placerat ultricies ante id dapibus. Donec imperdiet eros quis porttitor accumsan. Vestibulum ut nulla luctus velit feugiat elementum. Nam vel pharetra nisl. Nullam risus tellus, tempor quis ipsum et, pretium rutrum ipsum.\n' + + '\n' + + 'Fusce molestie leo at facilisis mollis. Vivamus iaculis facilisis fermentum. Vivamus blandit id nisi sit amet porta. Nunc luctus porta blandit. Sed ac consequat eros, eu fringilla lorem. In blandit pharetra sollicitudin. Vivamus placerat risus ut ex faucibus, nec vehicula sapien imperdiet. Praesent luctus, leo eget ultrices cursus, neque ante porttitor mauris, id tempus tellus urna at ex. Curabitur elementum id quam vitae condimentum. Proin sit amet magna vel metus blandit iaculis. Phasellus viverra libero in lacus gravida, id laoreet ligula dapibus. Cras commodo arcu eget mi dignissim, et lobortis elit faucibus. Suspendisse potenti. ' + ) + const rangeFrom = { from: { line: 7, ch: 14 }, to: { line: 7, ch: 14 } } + const rangeTo = { from: { line: 7, ch: 14 }, to: { line: 7, ch: 14 } } systemUnderTest.checkChangeRange(editor, rangeFrom, rangeTo) diff --git a/webpack-production.config.js b/webpack-production.config.js index bcfb2a42..9c5a0b42 100644 --- a/webpack-production.config.js +++ b/webpack-production.config.js @@ -14,7 +14,8 @@ var config = Object.assign({}, skeleton, { { test: /\.styl$/, exclude: /(node_modules|bower_components)/, - loader: 'style!css?modules&importLoaders=1&localIdentName=[name]__[local]___[path]!stylus?sourceMap' + loader: + 'style!css?modules&importLoaders=1&localIdentName=[name]__[local]___[path]!stylus?sourceMap' }, { test: /\.json$/, @@ -35,8 +36,8 @@ var config = Object.assign({}, skeleton, { new webpack.optimize.OccurenceOrderPlugin(), new webpack.DefinePlugin({ 'process.env': { - 'NODE_ENV': JSON.stringify('production'), - 'BABEL_ENV': JSON.stringify('production') + NODE_ENV: JSON.stringify('production'), + BABEL_ENV: JSON.stringify('production') } }) ] diff --git a/webpack.config.js b/webpack.config.js index ad3c4f8b..ac0b91db 100644 --- a/webpack.config.js +++ b/webpack.config.js @@ -12,7 +12,8 @@ var config = Object.assign({}, skeleton, { { test: /\.styl$/, exclude: /(node_modules|bower_components)/, - loader: 'style!css?modules&importLoaders=1&localIdentName=[name]__[local]___[path]!stylus?sourceMap' + loader: + 'style!css?modules&importLoaders=1&localIdentName=[name]__[local]___[path]!stylus?sourceMap' }, { test: /\.json$/, @@ -39,4 +40,3 @@ var config = Object.assign({}, skeleton, { }) module.exports = config - diff --git a/yarn.lock b/yarn.lock index ab5441b3..f73d0d21 100644 --- a/yarn.lock +++ b/yarn.lock @@ -193,6 +193,11 @@ ajv-keywords@^1.0.0: version "1.5.1" resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-1.5.1.tgz#314dd0a4b3368fad3dfcdc54ede6171b886daf3c" +ajv-keywords@^2.1.0: + version "2.1.1" + resolved "https://registry.yarnpkg.com/ajv-keywords/-/ajv-keywords-2.1.1.tgz#617997fc5f60576894c435f940d819e135b80762" + integrity sha1-YXmX/F9gV2iUxDX5QNgZ4TW4B2I= + ajv@^4.7.0: version "4.11.8" resolved "https://registry.yarnpkg.com/ajv/-/ajv-4.11.8.tgz#82ffb02b29e662ae53bdc20af15947706739c536" @@ -200,7 +205,7 @@ ajv@^4.7.0: co "^4.6.0" json-stable-stringify "^1.0.1" -ajv@^5.1.0: +ajv@^5.1.0, ajv@^5.2.3, ajv@^5.3.0: version "5.5.2" resolved "https://registry.yarnpkg.com/ajv/-/ajv-5.5.2.tgz#73b5eeca3fab653e3d3f9422b341ad42205dc965" dependencies: @@ -419,6 +424,20 @@ asar@^0.12.0: mksnapshot "^0.3.0" tmp "0.0.28" +asar@^0.13.0: + version "0.13.1" + resolved "https://registry.yarnpkg.com/asar/-/asar-0.13.1.tgz#dfc73f574a7db256b09ba62d1f0e95cd4a6cb8d3" + integrity sha512-HJnZadTbDVDhBDv3tMyDov3ZnwMYYmz80/+a7S6cFPvulUyRz55UG5hOyHeWSP1iZud6vjFq8GOYM6xxtxJECQ== + dependencies: + chromium-pickle-js "^0.2.0" + commander "^2.9.0" + cuint "^0.2.1" + glob "^6.0.4" + minimatch "^3.0.3" + mkdirp "^0.5.0" + mksnapshot "^0.3.0" + tmp "0.0.28" + asar@^0.14.0: version "0.14.3" resolved "https://registry.yarnpkg.com/asar/-/asar-0.14.3.tgz#c72a81542a48e3bca459fb1b07ee2b6adfae265d" @@ -432,22 +451,6 @@ asar@^0.14.0: mksnapshot "^0.3.0" tmp "0.0.28" -asar@^0.9.0: - version "0.9.1" - resolved "https://registry.yarnpkg.com/asar/-/asar-0.9.1.tgz#b2f2fe1b49c163013bdb229d6eba2d2078ff5cc4" - dependencies: - chromium-pickle-js "0.1.0" - commander "2.3.0" - cuint "0.1.5" - glob "^5.0.5" - minimatch "2.0.4" - mkdirp "^0.5.0" - mksnapshot "0.1.0" - -asn1@0.1.11: - version "0.1.11" - resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.1.11.tgz#559be18376d08a4ec4dbe80877d27818639b2df7" - asn1@~0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/asn1/-/asn1-0.2.3.tgz#dac8787713c9966849fc8180777ebe9c1ddf3b86" @@ -456,10 +459,6 @@ assert-plus@1.0.0, assert-plus@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-1.0.0.tgz#f12e0f3c5d77b0b1cdd9146942e4e96c1e4dd525" -assert-plus@^0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/assert-plus/-/assert-plus-0.1.5.tgz#ee74009413002d84cec7219c6ac811812e723160" - assert@^1.1.1: version "1.4.1" resolved "https://registry.yarnpkg.com/assert/-/assert-1.4.1.tgz#99912d591836b5a6f5b345c0f07eefc08fc65d91" @@ -482,11 +481,11 @@ async-limiter@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.0.tgz#78faed8c3d074ab81f22b4e985d79e8738f720f8" -async@^0.9.0, async@~0.9.0: +async@^0.9.0: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" -async@^1.3.0, async@^1.4.0, async@^1.4.2, async@^1.5.1, async@^1.5.2: +async@^1.3.0, async@^1.4.2, async@^1.5.2: version "1.5.2" resolved "https://registry.yarnpkg.com/async/-/async-1.5.2.tgz#ec6a61ae56480c0c3cb241c95618e20892f9672a" @@ -653,10 +652,6 @@ aws-sdk@>=2.2.37, aws-sdk@^2.48.0: uuid "3.1.0" xml2js "0.4.17" -aws-sign2@~0.5.0: - version "0.5.0" - resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.5.0.tgz#c57103f7a17fc037f02d7c2e64b602ea223f7d63" - aws-sign2@~0.7.0: version "0.7.0" resolved "https://registry.yarnpkg.com/aws-sign2/-/aws-sign2-0.7.0.tgz#b46e890934a9591f2d2f6f86d7e6a9f1b3fe76a8" @@ -665,7 +660,7 @@ aws4@^1.6.0: version "1.7.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.7.0.tgz#d4d0e9b9dbfca77bf08eeb0a8a471550fe39e289" -babel-code-frame@^6.16.0, babel-code-frame@^6.26.0: +babel-code-frame@^6.16.0, babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" dependencies: @@ -1354,16 +1349,6 @@ binary@^0.3.0: buffers "~0.1.1" chainsaw "~0.1.0" -bl@~0.9.0: - version "0.9.5" - resolved "https://registry.yarnpkg.com/bl/-/bl-0.9.5.tgz#c06b797af085ea00bc527afc8efcf11de2232054" - dependencies: - readable-stream "~1.0.26" - -bluebird@^2.9.30: - version "2.11.0" - resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-2.11.0.tgz#534b9033c022c9579c56ba3b3e5a5caafbb650e1" - bluebird@^3.0.0, bluebird@^3.1.1, bluebird@^3.3.4, bluebird@^3.5.0: version "3.5.1" resolved "https://registry.yarnpkg.com/bluebird/-/bluebird-3.5.1.tgz#d9551f9de98f1fcda1e683d17ee91a0602ee2eb9" @@ -1383,12 +1368,6 @@ body-parser@1.18.2: raw-body "2.3.2" type-is "~1.6.15" -boom@2.x.x: - version "2.10.1" - resolved "https://registry.yarnpkg.com/boom/-/boom-2.10.1.tgz#39c8918ceff5799f83f9492a848f625add0c766f" - dependencies: - hoek "2.x.x" - boxen@^1.2.1: version "1.3.0" resolved "https://registry.yarnpkg.com/boxen/-/boxen-1.3.0.tgz#55c6c39a8ba58d9c61ad22cd877532deb665a20b" @@ -1401,7 +1380,7 @@ boxen@^1.2.1: term-size "^1.2.0" widest-line "^2.0.0" -brace-expansion@^1.0.0, brace-expansion@^1.1.7: +brace-expansion@^1.1.7: version "1.1.11" resolved "https://registry.yarnpkg.com/brace-expansion/-/brace-expansion-1.1.11.tgz#3c7fcbf529d87226f3d2f52b966ff5271eb441dd" dependencies: @@ -1594,7 +1573,7 @@ camelcase@^1.0.2: version "1.2.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-1.2.1.tgz#9bb5304d2e0b56698b2c758b08a3eaa9daa58a39" -camelcase@^2.0.0, camelcase@^2.0.1: +camelcase@^2.0.0: version "2.1.1" resolved "https://registry.yarnpkg.com/camelcase/-/camelcase-2.1.1.tgz#7c1d16d679a1bbe59ca02cacecfb011e201f5a1f" @@ -1637,10 +1616,6 @@ caseless@~0.12.0: version "0.12.0" resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.12.0.tgz#1b681c21ff84033c826543090689420d187151dc" -caseless@~0.9.0: - version "0.9.0" - resolved "https://registry.yarnpkg.com/caseless/-/caseless-0.9.0.tgz#b7b65ce6bf1413886539cfd533f0b30effa9cf88" - center-align@^0.1.1: version "0.1.3" resolved "https://registry.yarnpkg.com/center-align/-/center-align-0.1.3.tgz#aa0d32629b6ee972200411cbd4461c907bc2b7ad" @@ -1691,6 +1666,20 @@ chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.3.2, chalk@^2.4.1: escape-string-regexp "^1.0.5" supports-color "^5.3.0" +chalk@^2.1.0: + version "2.4.2" + resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424" + integrity sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ== + dependencies: + ansi-styles "^3.2.1" + escape-string-regexp "^1.0.5" + supports-color "^5.3.0" + +chardet@^0.4.0: + version "0.4.2" + resolved "https://registry.yarnpkg.com/chardet/-/chardet-0.4.2.tgz#b5473b33dc97c424e5d98dc87d55d4d8a29c8bf2" + integrity sha1-tUc7M9yXxCTl2Y3IfVXU2KKci/I= + chart.js@^2.7.2: version "2.7.2" resolved "http://registry.npm.taobao.org/chart.js/download/chart.js-2.7.2.tgz#3c9fde4dc5b95608211bdefeda7e5d33dffa5714" @@ -1730,7 +1719,7 @@ chownr@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/chownr/-/chownr-1.0.1.tgz#e2a75042a9551908bebd25b8523d5f9769d79181" -chromium-pickle-js@0.1.0, chromium-pickle-js@^0.1.0: +chromium-pickle-js@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/chromium-pickle-js/-/chromium-pickle-js-0.1.0.tgz#1d48b107d82126a2f3e211c2ea25f803ba551b21" @@ -1823,7 +1812,7 @@ cliui@^2.1.0: right-align "^0.1.1" wordwrap "0.0.2" -cliui@^3.0.3, cliui@^3.2.0: +cliui@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/cliui/-/cliui-3.2.0.tgz#120601537a916d29940f934da3b48d585a39213d" dependencies: @@ -1977,20 +1966,10 @@ combined-stream@1.0.6, combined-stream@~1.0.5: dependencies: delayed-stream "~1.0.0" -combined-stream@~0.0.4, combined-stream@~0.0.5: - version "0.0.7" - resolved "https://registry.yarnpkg.com/combined-stream/-/combined-stream-0.0.7.tgz#0137e657baa5a7541c57ac37ac5fc07d73b4dc1f" - dependencies: - delayed-stream "0.0.5" - commander@2: version "2.16.0" resolved "http://registry.npm.taobao.org/commander/download/commander-2.16.0.tgz#f16390593996ceb4f3eeb020b31d78528f7f8a50" -commander@2.3.0: - version "2.3.0" - resolved "https://registry.yarnpkg.com/commander/-/commander-2.3.0.tgz#fd430e889832ec353b9acd1de217c11cb3eef873" - commander@2.6.0: version "2.6.0" resolved "https://registry.yarnpkg.com/commander/-/commander-2.6.0.tgz#9df7e52fb2a0cb0fb89058ee80c3104225f37e1d" @@ -2005,7 +1984,7 @@ commander@^2.20.0, commander@~2.20.3: resolved "https://registry.yarnpkg.com/commander/-/commander-2.20.3.tgz#fd485e84c03eb4881c20722ba48035e8531aeb33" integrity sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ== -commander@^2.8.1, commander@^2.9.0: +commander@^2.9.0: version "2.15.1" resolved "https://registry.yarnpkg.com/commander/-/commander-2.15.1.tgz#df46e867d0fc2aec66a34662b406a9ccafff5b0f" @@ -2051,7 +2030,7 @@ concat-map@0.0.1: version "0.0.1" resolved "https://registry.yarnpkg.com/concat-map/-/concat-map-0.0.1.tgz#d8a96bd77fd68df7793a73036a3ba0d5405d477b" -concat-stream@1.6.2, concat-stream@^1.4.6, concat-stream@^1.5.2: +concat-stream@1.6.2, concat-stream@^1.4.6, concat-stream@^1.5.2, concat-stream@^1.6.0: version "1.6.2" resolved "https://registry.yarnpkg.com/concat-stream/-/concat-stream-1.6.2.tgz#904bdf194cd3122fc675c77fc4ac3d4ff0fd1a34" dependencies: @@ -2232,7 +2211,7 @@ cross-env@^5.2.0: cross-spawn "^6.0.5" is-windows "^1.0.0" -cross-spawn@^5.0.1: +cross-spawn@^5.0.1, cross-spawn@^5.1.0: version "5.1.0" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-5.1.0.tgz#e8bd0efee58fcff6f8f94510a0a554bbfa235449" dependencies: @@ -2256,12 +2235,6 @@ cross-unzip@0.0.2: resolved "https://registry.yarnpkg.com/cross-unzip/-/cross-unzip-0.0.2.tgz#5183bc47a09559befcf98cc4657964999359372f" integrity sha1-UYO8R6CVWb78+YzEZXlkmZNZNy8= -cryptiles@2.x.x: - version "2.0.5" - resolved "https://registry.yarnpkg.com/cryptiles/-/cryptiles-2.0.5.tgz#3bdfecdc608147c1c67202fa291e7dca59eaa3b8" - dependencies: - boom "2.x.x" - crypto-browserify@3.3.0: version "3.3.0" resolved "https://registry.yarnpkg.com/crypto-browserify/-/crypto-browserify-3.3.0.tgz#b9fc75bb4a0ed61dcf1cd5dae96eb30c9c3e506c" @@ -2400,14 +2373,6 @@ cssstyle@^1.0.0: dependencies: cssom "0.3.x" -ctype@0.5.3: - version "0.5.3" - resolved "https://registry.yarnpkg.com/ctype/-/ctype-0.5.3.tgz#82c18c2461f74114ef16c135224ad0b9144ca12f" - -cuint@0.1.5: - version "0.1.5" - resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.1.5.tgz#b848b18466f3f180f96d1eb6e07ccb7ecf126a2e" - cuint@^0.2.1: version "0.2.2" resolved "https://registry.yarnpkg.com/cuint/-/cuint-0.2.2.tgz#408086d409550c2631155619e9fa7bcadc3b991b" @@ -2661,7 +2626,7 @@ d@1: dependencies: es5-ext "^0.10.9" -"dagre-d3@github:dagrejs/dagre-d3": +dagre-d3@dagrejs/dagre-d3: version "0.6.4-pre" resolved "https://codeload.github.com/dagrejs/dagre-d3/tar.gz/e1a00e5cb518f5d2304a35647e024f31d178e55b" dependencies: @@ -2745,18 +2710,6 @@ decode-uri-component@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/decode-uri-component/-/decode-uri-component-0.2.0.tgz#eb3913333458775cb84cd1a1fae062106bb87545" -decompress-zip@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/decompress-zip/-/decompress-zip-0.1.0.tgz#bce60c11664f2d660fca4bcf634af6de5d6c14c7" - dependencies: - binary "^0.3.0" - graceful-fs "^3.0.0" - mkpath "^0.1.0" - nopt "^3.0.1" - q "^1.1.2" - readable-stream "^1.1.8" - touch "0.0.3" - decompress-zip@0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/decompress-zip/-/decompress-zip-0.3.0.tgz#ae3bcb7e34c65879adfe77e19c30f86602b4bdb0" @@ -2840,10 +2793,6 @@ del@^2.0.2: pinkie-promise "^2.0.0" rimraf "^2.2.8" -delayed-stream@0.0.5: - version "0.0.5" - resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-0.0.5.tgz#d4b1f43a93e8296dfe02694f4680bc37a313c73f" - delayed-stream@~1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/delayed-stream/-/delayed-stream-1.0.0.tgz#df3ae199acadfb7d440aaae0b29e2272b24ec619" @@ -2905,7 +2854,7 @@ doctrine@^1.2.2: esutils "^2.0.2" isarray "^1.0.0" -doctrine@^2.0.0, doctrine@^2.0.2: +doctrine@^2.0.2, doctrine@^2.1.0: version "2.1.0" resolved "https://registry.yarnpkg.com/doctrine/-/doctrine-2.1.0.tgz#5cd01fc101621b42c4cd7f5d1a66243716d3f39d" dependencies: @@ -3036,20 +2985,21 @@ electron-gh-releases@^2.0.4: got "^5.1.0" semver "^5.1.0" -electron-installer-debian@^0.1.0: - version "0.1.1" - resolved "https://registry.yarnpkg.com/electron-installer-debian/-/electron-installer-debian-0.1.1.tgz#c6d4e576d7959f49d17b4873ff9f66738c844cae" +electron-installer-debian@^0.5.0: + version "0.5.2" + resolved "https://registry.yarnpkg.com/electron-installer-debian/-/electron-installer-debian-0.5.2.tgz#f1a11a3c4f60eb411e06fd929fe6d8d124f9fa06" + integrity sha1-8aEaPE9g60EeBv2Sn+bY0ST5+gY= dependencies: - asar "^0.9.0" - async "^1.5.1" + asar "^0.13.0" + async "^2.0.0" debug "^2.2.0" - fs-extra "^0.26.0" + fs-extra "^1.0.0" get-folder-size "^1.0.0" - glob "^6.0.3" - lodash "^4.0.1" + glob "^7.0.0" + lodash "^4.13.0" temp "^0.8.3" word-wrap "^1.1.0" - yargs "^3.32.0" + yargs "^7.0.2" electron-installer-redhat@^0.3.0: version "0.3.1" @@ -3142,6 +3092,11 @@ electron@4: electron-download "^4.1.0" extract-zip "^1.0.3" +emoji-regex@^6.4.1: + version "6.5.1" + resolved "https://registry.yarnpkg.com/emoji-regex/-/emoji-regex-6.5.1.tgz#9baea929b155565c11ea41c6626eaa65cef992c2" + integrity sha512-PAHp6TxrCy7MGMFidro8uikr+zlJJKJ/Q6mm2ExZ7HwkyR9lSVFfE3kt36qcwa24BQL7y0G9axycGjK1A/0uNQ== + emojis-list@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/emojis-list/-/emojis-list-2.1.0.tgz#4daa4d9db00f9819880c79fa457ae5b09a1fd389" @@ -3324,6 +3279,13 @@ escope@^3.6.0: esrecurse "^4.1.0" estraverse "^4.1.1" +eslint-config-prettier@^6.10.0: + version "6.10.0" + resolved "https://registry.yarnpkg.com/eslint-config-prettier/-/eslint-config-prettier-6.10.0.tgz#7b15e303bf9c956875c948f6b21500e48ded6a7f" + integrity sha512-AtndijGte1rPILInUdHjvKEGbIV06NuvPrqlIEaEaWtbtvJh464mDeyGMdZEQMsGvC0ZVkiex1fSNcC4HAbRGg== + dependencies: + get-stdin "^6.0.0" + eslint-config-standard-jsx@3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/eslint-config-standard-jsx/-/eslint-config-standard-jsx-3.2.0.tgz#c240e26ed919a11a42aa4de8059472b38268d620" @@ -3336,6 +3298,13 @@ eslint-config-standard@6.2.1, eslint-config-standard@^6.2.1: version "6.2.1" resolved "https://registry.yarnpkg.com/eslint-config-standard/-/eslint-config-standard-6.2.1.tgz#d3a68aafc7191639e7ee441e7348739026354292" +eslint-plugin-prettier@^3.1.2: + version "3.1.2" + resolved "https://registry.yarnpkg.com/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.2.tgz#432e5a667666ab84ce72f945c72f77d996a5c9ba" + integrity sha512-GlolCC9y3XZfv3RQfwGew7NnuFDKsfI4lbvRK+PIIo23SFH+LemGs4cKwzAaRa+Mdb+lQO/STaIayno8T5sJJA== + dependencies: + prettier-linter-helpers "^1.0.0" + eslint-plugin-promise@~3.4.0: version "3.4.2" resolved "https://registry.yarnpkg.com/eslint-plugin-promise/-/eslint-plugin-promise-3.4.2.tgz#1be2793eafe2d18b5b123b8136c269f804fe7122" @@ -3364,45 +3333,62 @@ eslint-plugin-standard@~2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/eslint-plugin-standard/-/eslint-plugin-standard-2.0.1.tgz#3589699ff9c917f2c25f76a916687f641c369ff3" -eslint@^3.13.1: - version "3.19.0" - resolved "https://registry.yarnpkg.com/eslint/-/eslint-3.19.0.tgz#c8fc6201c7f40dd08941b87c085767386a679acc" +eslint-scope@^3.7.1: + version "3.7.3" + resolved "https://registry.yarnpkg.com/eslint-scope/-/eslint-scope-3.7.3.tgz#bb507200d3d17f60247636160b4826284b108535" + integrity sha512-W+B0SvF4gamyCTmUc+uITPY0989iXVfKvhwtmJocTaYoc/3khEHmEmvfY/Gn9HA9VV75jrQECsHizkNw1b68FA== dependencies: - babel-code-frame "^6.16.0" - chalk "^1.1.3" - concat-stream "^1.5.2" - debug "^2.1.1" - doctrine "^2.0.0" - escope "^3.6.0" - espree "^3.4.0" + esrecurse "^4.1.0" + estraverse "^4.1.1" + +eslint-visitor-keys@^1.0.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/eslint-visitor-keys/-/eslint-visitor-keys-1.1.0.tgz#e2a82cea84ff246ad6fb57f9bde5b46621459ec2" + integrity sha512-8y9YjtM1JBJU/A9Kc+SbaOV4y29sSWckBwMHa+FGtVj5gN/sbnKDf6xJUl+8g7FAij9LVaP8C24DUiH/f/2Z9A== + +eslint@^4.18.2: + version "4.19.1" + resolved "https://registry.yarnpkg.com/eslint/-/eslint-4.19.1.tgz#32d1d653e1d90408854bfb296f076ec7e186a300" + integrity sha512-bT3/1x1EbZB7phzYu7vCr1v3ONuzDtX8WjuM9c0iYxe+cq+pwcKEoQjl7zd3RpC6YOLgnSy3cTN58M2jcoPDIQ== + dependencies: + ajv "^5.3.0" + babel-code-frame "^6.22.0" + chalk "^2.1.0" + concat-stream "^1.6.0" + cross-spawn "^5.1.0" + debug "^3.1.0" + doctrine "^2.1.0" + eslint-scope "^3.7.1" + eslint-visitor-keys "^1.0.0" + espree "^3.5.4" esquery "^1.0.0" - estraverse "^4.2.0" esutils "^2.0.2" file-entry-cache "^2.0.0" - glob "^7.0.3" - globals "^9.14.0" - ignore "^3.2.0" + functional-red-black-tree "^1.0.1" + glob "^7.1.2" + globals "^11.0.1" + ignore "^3.3.3" imurmurhash "^0.1.4" - inquirer "^0.12.0" - is-my-json-valid "^2.10.0" + inquirer "^3.0.6" is-resolvable "^1.0.0" - js-yaml "^3.5.1" - json-stable-stringify "^1.0.0" + js-yaml "^3.9.1" + json-stable-stringify-without-jsonify "^1.0.1" levn "^0.3.0" - lodash "^4.0.0" - mkdirp "^0.5.0" + lodash "^4.17.4" + minimatch "^3.0.2" + mkdirp "^0.5.1" natural-compare "^1.4.0" optionator "^0.8.2" - path-is-inside "^1.0.1" - pluralize "^1.2.1" - progress "^1.1.8" - require-uncached "^1.0.2" - shelljs "^0.7.5" - strip-bom "^3.0.0" + path-is-inside "^1.0.2" + pluralize "^7.0.0" + progress "^2.0.0" + regexpp "^1.0.1" + require-uncached "^1.0.3" + semver "^5.3.0" + strip-ansi "^4.0.0" strip-json-comments "~2.0.1" - table "^3.7.8" + table "4.0.2" text-table "~0.2.0" - user-home "^2.0.0" eslint@~3.10.2: version "3.10.2" @@ -3452,7 +3438,7 @@ espower-location-detector@^1.0.0: source-map "^0.5.0" xtend "^4.0.0" -espree@^3.3.1, espree@^3.4.0: +espree@^3.3.1, espree@^3.5.4: version "3.5.4" resolved "https://registry.yarnpkg.com/espree/-/espree-3.5.4.tgz#b0f447187c8a8bed944b815a660bddf5deb5d1a7" dependencies: @@ -3664,8 +3650,18 @@ extend-shallow@^3.0.0, extend-shallow@^3.0.2: is-extendable "^1.0.1" extend@~3.0.1: - version "3.0.1" - resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" + version "3.0.2" + resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.2.tgz#f8b1136b4071fbd8eb140aff858b1019ec2915fa" + integrity sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g== + +external-editor@^2.0.4: + version "2.2.0" + resolved "https://registry.yarnpkg.com/external-editor/-/external-editor-2.2.0.tgz#045511cfd8d133f3846673d1047c154e214ad3d5" + integrity sha512-bSn6gvGxKt+b7+6TKEv1ZycHleA7aHhRHyAqJyp5pbUFuYYNIzpZnQDk7AsYckyWdEnTeAnay0aCy2aV6iTk9A== + dependencies: + chardet "^0.4.0" + iconv-lite "^0.4.17" + tmp "^0.0.33" extglob@^0.3.1: version "0.3.2" @@ -3715,6 +3711,11 @@ fast-diff@^1.1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.1.2.tgz#4b62c42b8e03de3f848460b639079920695d0154" +fast-diff@^1.1.2: + version "1.2.0" + resolved "https://registry.yarnpkg.com/fast-diff/-/fast-diff-1.2.0.tgz#73ee11982d86caaf7959828d519cfe927fac5f03" + integrity sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w== + fast-json-stable-stringify@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/fast-json-stable-stringify/-/fast-json-stable-stringify-2.0.0.tgz#d5142c0caee6b1189f87d3a76111064f86c8bbf2" @@ -3947,6 +3948,7 @@ font-awesome@^4.3.0: for-in@^1.0.1, for-in@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/for-in/-/for-in-1.0.2.tgz#81068d295a8142ec0ac726c6e2200c30fb6d5e80" + integrity sha1-gQaNKVqBQuwKxybG4iAMMPttXoA= for-own@^0.1.4: version "0.1.5" @@ -3958,18 +3960,10 @@ foreach@^2.0.5: version "2.0.5" resolved "https://registry.yarnpkg.com/foreach/-/foreach-2.0.5.tgz#0bee005018aeb260d0a3af3ae658dd0136ec1b99" -forever-agent@~0.6.0, forever-agent@~0.6.1: +forever-agent@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/forever-agent/-/forever-agent-0.6.1.tgz#fbc71f0c41adeb37f96c577ad1ed42d8fdacca91" -form-data@~0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/form-data/-/form-data-0.2.0.tgz#26f8bc26da6440e299cbdcfb69035c4f77a6e466" - dependencies: - async "~0.9.0" - combined-stream "~0.0.4" - mime-types "~2.0.3" - form-data@~2.3.1: version "2.3.2" resolved "https://registry.yarnpkg.com/form-data/-/form-data-2.3.2.tgz#4970498be604c20c005d4f5c23aecd21d6b49099" @@ -3992,15 +3986,7 @@ fresh@0.5.2: version "0.5.2" resolved "https://registry.yarnpkg.com/fresh/-/fresh-0.5.2.tgz#3d8cadd90d976569fa835ab1f8e4b23a105605a7" -fs-extra@0.18.2: - version "0.18.2" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.18.2.tgz#af05ca702b0b6dfa7de803a1f7ab479ec5c21525" - dependencies: - graceful-fs "^3.0.5" - jsonfile "^2.0.0" - rimraf "^2.2.8" - -fs-extra@0.26.7, fs-extra@^0.26.0, fs-extra@^0.26.7: +fs-extra@0.26.7, fs-extra@^0.26.7: version "0.26.7" resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-0.26.7.tgz#9ae1fdd94897798edab76d0918cf42d0c3184fa9" dependencies: @@ -4068,6 +4054,11 @@ function-name-support@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/function-name-support/-/function-name-support-0.2.0.tgz#55d3bfaa6eafd505a50f9bc81fdf57564a0bb071" +functional-red-black-tree@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz#1b0ab3bd553b2a0d6399d29c0e3ea0b252078327" + integrity sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc= + galactus@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/galactus/-/galactus-0.2.1.tgz#cbed2d20a40c1f5679a35908e2b9415733e78db9" @@ -4188,17 +4179,7 @@ glob@7.0.x: once "^1.3.0" path-is-absolute "^1.0.0" -glob@^5.0.5: - version "5.0.15" - resolved "https://registry.yarnpkg.com/glob/-/glob-5.0.15.tgz#1bc936b9e02f4a603fcc222ecf7633d30b8b93b1" - dependencies: - inflight "^1.0.4" - inherits "2" - minimatch "2 || 3" - once "^1.3.0" - path-is-absolute "^1.0.0" - -glob@^6.0.3, glob@^6.0.4: +glob@^6.0.4: version "6.0.4" resolved "https://registry.yarnpkg.com/glob/-/glob-6.0.4.tgz#0f08860f6a155127b2fadd4f9ce24b1aab6e4d22" dependencies: @@ -4240,7 +4221,12 @@ global@^4.3.0: min-document "^2.19.0" process "~0.5.1" -globals@^9.14.0, globals@^9.18.0, globals@^9.2.0: +globals@^11.0.1: + version "11.12.0" + resolved "https://registry.yarnpkg.com/globals/-/globals-11.12.0.tgz#ab8795338868a0babd8525758018c2a7eb95c42e" + integrity sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA== + +globals@^9.18.0, globals@^9.2.0: version "9.18.0" resolved "https://registry.yarnpkg.com/globals/-/globals-9.18.0.tgz#aa3896b3e69b487f17e31ed2143d69a8e30c2d8a" @@ -4301,12 +4287,6 @@ got@^6.7.1: unzip-response "^2.0.1" url-parse-lax "^1.0.0" -graceful-fs@^3.0.0, graceful-fs@^3.0.5: - version "3.0.11" - resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-3.0.11.tgz#7613c778a1afea62f25c630a086d7f3acbbdd818" - dependencies: - natives "^1.1.0" - graceful-fs@^4.1.11, graceful-fs@^4.1.2, graceful-fs@^4.1.3, graceful-fs@^4.1.6, graceful-fs@^4.1.9: version "4.1.11" resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.1.11.tgz#0e8bdfe4d1ddb8854d64e04ea7c00e2a026e5658" @@ -4336,11 +4316,12 @@ growly@^1.3.0: version "1.3.0" resolved "https://registry.yarnpkg.com/growly/-/growly-1.3.0.tgz#f10748cbe76af964b7c96c93c6bcc28af120c081" -grunt-electron-installer-debian@^0.2.0: - version "0.2.0" - resolved "https://registry.yarnpkg.com/grunt-electron-installer-debian/-/grunt-electron-installer-debian-0.2.0.tgz#2849f7c2df6b8adf835d58f09c6aa01550254be0" +grunt-electron-installer-debian@^0.5.0: + version "0.5.0" + resolved "https://registry.yarnpkg.com/grunt-electron-installer-debian/-/grunt-electron-installer-debian-0.5.0.tgz#30ab659222634ebb5711e07b4eb331f7bd28eb96" + integrity sha1-MKtlkiJjTrtXEeB7TrMx970o65Y= dependencies: - electron-installer-debian "^0.1.0" + electron-installer-debian "^0.5.0" grunt-electron-installer-redhat@^0.3.1: version "0.3.1" @@ -4415,28 +4396,20 @@ gud@^1.0.0: integrity sha512-zGEOVKFM5sVPPrYs7J5/hYEw2Pof8KCyOwyhG8sAF26mCAeUFAcYPu1mwB7hhpIP29zOIBaDqwuHdLp0jvZXjw== handlebars@^4.0.3: - version "4.0.11" - resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.0.11.tgz#630a35dfe0294bc281edae6ffc5d329fc7982dcc" + version "4.5.3" + resolved "https://registry.yarnpkg.com/handlebars/-/handlebars-4.5.3.tgz#5cf75bd8714f7605713511a56be7c349becb0482" + integrity sha512-3yPecJoJHK/4c6aZhSvxOyG4vJKDshV36VHp0iVCDVh7o9w2vwi3NSnL2MMPj3YdduqaBcu7cGbggJQM0br9xA== dependencies: - async "^1.4.0" + neo-async "^2.6.0" optimist "^0.6.1" - source-map "^0.4.4" + source-map "^0.6.1" optionalDependencies: - uglify-js "^2.6" + uglify-js "^3.1.4" har-schema@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/har-schema/-/har-schema-2.0.0.tgz#a94c2224ebcac04782a0d9035521f24735b7ec92" -har-validator@^1.4.0: - version "1.8.0" - resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-1.8.0.tgz#d83842b0eb4c435960aeb108a067a3aa94c0eeb2" - dependencies: - bluebird "^2.9.30" - chalk "^1.0.0" - commander "^2.8.1" - is-my-json-valid "^2.12.0" - har-validator@~5.0.3: version "5.0.3" resolved "https://registry.yarnpkg.com/har-validator/-/har-validator-5.0.3.tgz#ba402c266194f15956ef15e0fcf242993f6a7dfd" @@ -4517,15 +4490,6 @@ has@^1.0.1: dependencies: function-bind "^1.0.2" -hawk@~2.3.0: - version "2.3.1" - resolved "https://registry.yarnpkg.com/hawk/-/hawk-2.3.1.tgz#1e731ce39447fa1d0f6d707f7bceebec0fd1ec1f" - dependencies: - boom "2.x.x" - cryptiles "2.x.x" - hoek "2.x.x" - sntp "1.x.x" - he@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/he/-/he-1.2.0.tgz#84ae65fa7eafb165fddb61566ae14baf05664f0f" @@ -4552,10 +4516,6 @@ history@^4.9.0: tiny-warning "^1.0.0" value-equal "^0.4.0" -hoek@2.x.x: - version "2.16.3" - resolved "https://registry.yarnpkg.com/hoek/-/hoek-2.16.3.tgz#20bb7403d3cea398e91dc4710a8ff1b8274a25ed" - hoist-non-react-statics@^2.5.5: version "2.5.5" resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-2.5.5.tgz#c5903cf409c0dfd908f388e619d86b9c1174cb47" @@ -4656,14 +4616,6 @@ http-proxy@^1.16.2: follow-redirects "^1.0.0" requires-port "^1.0.0" -http-signature@~0.10.0: - version "0.10.1" - resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-0.10.1.tgz#4fbdac132559aa8323121e540779c0a012b27e66" - dependencies: - asn1 "0.1.11" - assert-plus "^0.1.5" - ctype "0.5.3" - http-signature@~1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/http-signature/-/http-signature-1.2.0.tgz#9aecd925114772f3d95b65a60abb8f7c18fbace1" @@ -4731,6 +4683,13 @@ iconv-lite@0.4.19: version "0.4.19" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.19.tgz#f7468f60135f5e5dad3399c0a81be9a1603a082b" +iconv-lite@^0.4.17: + version "0.4.24" + resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.4.24.tgz#2022b4b25fbddc21d2f524974a474aafe733908b" + integrity sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA== + dependencies: + safer-buffer ">= 2.1.2 < 3" + iconv-lite@~0.2.11: version "0.2.11" resolved "https://registry.yarnpkg.com/iconv-lite/-/iconv-lite-0.2.11.tgz#1ce60a3a57864a292d1321ff4609ca4bb965adc8" @@ -4763,6 +4722,11 @@ ignore@^3.0.9, ignore@^3.2.0: version "3.3.8" resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.8.tgz#3f8e9c35d38708a3a7e0e9abb6c73e7ee7707b2b" +ignore@^3.3.3: + version "3.3.10" + resolved "https://registry.yarnpkg.com/ignore/-/ignore-3.3.10.tgz#0a97fb876986e8081c631160f8f9f389157f0043" + integrity sha512-Pgs951kaMm5GXP7MOvxERINe3gsaVjUWFm+UZPSq9xYriQAksyhg0csnS0KXSNRD5NmNdapXEpjxG49+AKh/ug== + immutable@^3.8.1: version "3.8.2" resolved "https://registry.yarnpkg.com/immutable/-/immutable-3.8.2.tgz#c2439951455bb39913daf281376f1530e104adf3" @@ -4848,6 +4812,26 @@ inquirer@^0.12.0: strip-ansi "^3.0.0" through "^2.3.6" +inquirer@^3.0.6: + version "3.3.0" + resolved "https://registry.yarnpkg.com/inquirer/-/inquirer-3.3.0.tgz#9dd2f2ad765dcab1ff0443b491442a20ba227dc9" + integrity sha512-h+xtnyk4EwKvFWHrUYsWErEVR+igKtLdchu+o0Z1RL7VU/jVMFbYir2bp6bAj8efFNxWqHX0dIss6fJQ+/+qeQ== + dependencies: + ansi-escapes "^3.0.0" + chalk "^2.0.0" + cli-cursor "^2.1.0" + cli-width "^2.0.0" + external-editor "^2.0.4" + figures "^2.0.0" + lodash "^4.3.0" + mute-stream "0.0.7" + run-async "^2.2.0" + rx-lite "^4.0.8" + rx-lite-aggregates "^4.0.8" + string-width "^2.1.0" + strip-ansi "^4.0.0" + through "^2.3.6" + interpret@^0.6.4: version "0.6.6" resolved "https://registry.yarnpkg.com/interpret/-/interpret-0.6.6.tgz#fecd7a18e7ce5ca6abfb953e1f86213a49f1625b" @@ -4992,6 +4976,7 @@ is-extendable@^0.1.0, is-extendable@^0.1.1: is-extendable@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/is-extendable/-/is-extendable-1.0.1.tgz#a7470f9e426733d81bd81e1155264e3a3507cab4" + integrity sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA== dependencies: is-plain-object "^2.0.4" @@ -5046,7 +5031,7 @@ is-my-ip-valid@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-my-ip-valid/-/is-my-ip-valid-1.0.0.tgz#7b351b8e8edd4d3995d4d066680e664d94696824" -is-my-json-valid@^2.10.0, is-my-json-valid@^2.12.0: +is-my-json-valid@^2.10.0: version "2.17.2" resolved "https://registry.yarnpkg.com/is-my-json-valid/-/is-my-json-valid-2.17.2.tgz#6b2103a288e94ef3de5cf15d29dd85fc4b78d65c" dependencies: @@ -5121,6 +5106,7 @@ is-plain-obj@^1.0.0: is-plain-object@^2.0.1, is-plain-object@^2.0.3, is-plain-object@^2.0.4: version "2.0.4" resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677" + integrity sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og== dependencies: isobject "^3.0.1" @@ -5224,6 +5210,7 @@ isobject@^2.0.0: isobject@^3.0.0, isobject@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/isobject/-/isobject-3.0.1.tgz#4e431e92b11a9731636aa1f9c8d1ccbcfdab78df" + integrity sha1-TkMekrEalzFjaqH5yNHMvP2reN8= isomorphic-fetch@^2.1.1: version "2.2.1" @@ -5232,7 +5219,7 @@ isomorphic-fetch@^2.1.1: node-fetch "^1.0.1" whatwg-fetch ">=0.10.0" -isstream@~0.1.1, isstream@~0.1.2: +isstream@~0.1.2: version "0.1.2" resolved "https://registry.yarnpkg.com/isstream/-/isstream-0.1.2.tgz#47e63f7af55afa6f92e1500e690eb8b8529c099a" @@ -5603,7 +5590,7 @@ js-tokens@^3.0.0, js-tokens@^3.0.2: version "4.0.0" resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" -js-yaml@^3.10.0, js-yaml@^3.13.1, js-yaml@^3.5.1, js-yaml@^3.7.0, js-yaml@^3.8.1, js-yaml@^3.9.0: +js-yaml@^3.10.0, js-yaml@^3.13.1, js-yaml@^3.5.1, js-yaml@^3.7.0, js-yaml@^3.8.1, js-yaml@^3.9.0, js-yaml@^3.9.1: version "3.13.1" resolved "https://registry.yarnpkg.com/js-yaml/-/js-yaml-3.13.1.tgz#aff151b30bfdfa8e49e05da22e7415e9dfa37847" integrity sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw== @@ -5770,13 +5757,18 @@ json-schema@0.2.3: version "0.2.3" resolved "https://registry.yarnpkg.com/json-schema/-/json-schema-0.2.3.tgz#b480c892e59a2f05954ce727bd3f2a4e882f9e13" +json-stable-stringify-without-jsonify@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz#9db7b59496ad3f3cfef30a75142d2d930ad72651" + integrity sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE= + json-stable-stringify@^1.0.0, json-stable-stringify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/json-stable-stringify/-/json-stable-stringify-1.0.1.tgz#9a759d39c5f2ff503fd5300646ed445f88c4f9af" dependencies: jsonify "~0.0.0" -json-stringify-safe@~5.0.0, json-stringify-safe@~5.0.1: +json-stringify-safe@~5.0.1: version "5.0.1" resolved "https://registry.yarnpkg.com/json-stringify-safe/-/json-stringify-safe-5.0.1.tgz#1296a2d58fd45f19a0f6ce01d65701e2c735b6eb" @@ -5788,7 +5780,7 @@ json5@^0.5.0, json5@^0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/json5/-/json5-0.5.1.tgz#1eade7acc012034ad84e2396767ead9fa5495821" -jsonfile@^2.0.0, jsonfile@^2.1.0: +jsonfile@^2.1.0: version "2.4.0" resolved "https://registry.yarnpkg.com/jsonfile/-/jsonfile-2.4.0.tgz#3736a2b428b87bbda0cc83b53fa3d633a35c2ae8" optionalDependencies: @@ -6045,7 +6037,7 @@ lodash.escaperegexp@^4.1.2: version "4.1.2" resolved "https://registry.yarnpkg.com/lodash.escaperegexp/-/lodash.escaperegexp-4.1.2.tgz#64762c48618082518ac3df4ccf5d5886dae20347" -lodash.flatten@^4.2.0: +lodash.flatten@^4.2.0, lodash.flatten@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.flatten/-/lodash.flatten-4.4.0.tgz#f31c22225a9632d2bbf8e4addbef240aa765a61f" @@ -6116,7 +6108,7 @@ lodash@^4.0.0, lodash@^4.0.1, lodash@^4.12.0, lodash@^4.13.1, lodash@^4.16.6, lo resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.13.tgz#0bdc3a6adc873d2f4e0c4bac285df91b64fc7b93" integrity sha512-vm3/XWXfWtRua0FkUyEHBZy8kCPjErNBT9fJx8Zvs+U6zjqPbTUOpkaoum3O5uiA8sm+yNMHXfYkTUHFoMxFNA== -lodash@^4.17.11, lodash@^4.17.15: +lodash@^4.13.0, lodash@^4.17.11, lodash@^4.17.15: version "4.17.15" resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548" integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A== @@ -6399,8 +6391,9 @@ merge-stream@^1.0.0, merge-stream@^1.0.1: readable-stream "^2.0.1" merge@^1.1.3: - version "1.2.0" - resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.0.tgz#7531e39d4949c281a66b8c5a6e0265e8b05894da" + version "1.2.1" + resolved "https://registry.yarnpkg.com/merge/-/merge-1.2.1.tgz#38bebf80c3220a8a487b6fcfb3941bb11720c145" + integrity sha512-VjFo4P5Whtj4vsLzsYBu5ayHhoHJ0UqNm7ibvShmbmoz7tGi0vXaoJbGdB+GmDMLUdg8DpQXEIeVDAe8MaABvQ== mermaid@^8.4.2: version "8.4.2" @@ -6464,22 +6457,12 @@ micromatch@^3.1.4, micromatch@^3.1.8: version "1.33.0" resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.33.0.tgz#a3492050a5cb9b63450541e39d9788d2272783db" -mime-db@~1.12.0: - version "1.12.0" - resolved "https://registry.yarnpkg.com/mime-db/-/mime-db-1.12.0.tgz#3d0c63180f458eb10d325aaa37d7c58ae312e9d7" - mime-types@^2.1.12, mime-types@~2.1.17, mime-types@~2.1.18: version "2.1.18" resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.1.18.tgz#6f323f60a83d11146f831ff11fd66e2fe5503bb8" dependencies: mime-db "~1.33.0" -mime-types@~2.0.1, mime-types@~2.0.3: - version "2.0.14" - resolved "https://registry.yarnpkg.com/mime-types/-/mime-types-2.0.14.tgz#310e159db23e077f8bb22b748dabfa4957140aa6" - dependencies: - mime-db "~1.12.0" - mime@1.4.1: version "1.4.1" resolved "https://registry.yarnpkg.com/mime/-/mime-1.4.1.tgz#121f9ebc49e3766f311a76e1fa1c8003c4b03aa6" @@ -6524,12 +6507,6 @@ minimatch@0.3: dependencies: brace-expansion "^1.1.7" -minimatch@2.0.4: - version "2.0.4" - resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-2.0.4.tgz#83bea115803e7a097a78022427287edb762fafed" - dependencies: - brace-expansion "^1.0.0" - minimatch@~0.2.11, minimatch@~0.2.12: version "0.2.14" resolved "https://registry.yarnpkg.com/minimatch/-/minimatch-0.2.14.tgz#c74e780574f63c6f9a090e90efbe6ef53a6a756a" @@ -6548,6 +6525,7 @@ minimist@^1.1.0, minimist@^1.1.1, minimist@^1.1.3, minimist@^1.2.0: minimist@~0.0.1: version "0.0.10" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" + integrity sha1-3j+YVD2/lggr5IrRoMfNqDYwHc8= minipass@^2.2.1, minipass@^2.3.3: version "2.3.3" @@ -6563,8 +6541,9 @@ minizlib@^1.1.0: minipass "^2.2.1" mixin-deep@^1.1.3, mixin-deep@^1.2.0: - version "1.3.1" - resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.1.tgz#a49e7268dce1a0d9698e45326c5626df3543d0fe" + version "1.3.2" + resolved "https://registry.yarnpkg.com/mixin-deep/-/mixin-deep-1.3.2.tgz#1120b43dc359a785dce65b55b82e257ccf479566" + integrity sha512-WRoDn//mXBiJ1H40rqa3vH0toePwSsGb45iInWlTySa+Uu4k3tYUSxa2v1KqAiLtvlrSzaExqS1gtk96A9zvEA== dependencies: for-in "^1.0.2" is-extendable "^1.0.1" @@ -6579,14 +6558,6 @@ mkpath@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/mkpath/-/mkpath-0.1.0.tgz#7554a6f8d871834cc97b5462b122c4c124d6de91" -mksnapshot@0.1.0: - version "0.1.0" - resolved "https://registry.yarnpkg.com/mksnapshot/-/mksnapshot-0.1.0.tgz#f7d09abca806ad8c3780da701bb18778d7ce69ac" - dependencies: - decompress-zip "0.1.0" - fs-extra "0.18.2" - request "2.55.0" - mksnapshot@^0.3.0: version "0.3.1" resolved "https://registry.yarnpkg.com/mksnapshot/-/mksnapshot-0.3.1.tgz#2501c05657436d742ce958a4ff92c77e40dd37e6" @@ -6650,6 +6621,11 @@ mute-stream@0.0.5: version "0.0.5" resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.5.tgz#8fbfabb0a98a253d3184331f9e8deb7372fac6c0" +mute-stream@0.0.7: + version "0.0.7" + resolved "https://registry.yarnpkg.com/mute-stream/-/mute-stream-0.0.7.tgz#3075ce93bc21b8fab43e1bc4da7e8115ed1e7bab" + integrity sha1-MHXOk7whuPq0PhvE2n6BFe0ee6s= + nan@^2.9.2: version "2.10.0" resolved "https://registry.yarnpkg.com/nan/-/nan-2.10.0.tgz#96d0cd610ebd58d4b4de9cc0c6828cda99c7548f" @@ -6671,10 +6647,6 @@ nanomatch@^1.2.9: snapdragon "^0.8.1" to-regex "^3.0.1" -natives@^1.1.0: - version "1.1.4" - resolved "https://registry.yarnpkg.com/natives/-/natives-1.1.4.tgz#2f0f224fc9a7dd53407c7667c84cf8dbe773de58" - natural-compare@^1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/natural-compare/-/natural-compare-1.4.0.tgz#4abebfeed7541f2c27acfb29bdbbd15c8d5ba4f7" @@ -6691,6 +6663,11 @@ negotiator@0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/negotiator/-/negotiator-0.6.1.tgz#2b327184e8992101177b28563fb5e7102acd0ca9" +neo-async@^2.6.0: + version "2.6.1" + resolved "https://registry.yarnpkg.com/neo-async/-/neo-async-2.6.1.tgz#ac27ada66167fa8849a6addd837f6b189ad2081c" + integrity sha512-iyam8fBuCUpWeKPGpaNMetEocMt364qkCsfL9JuhjXX6dRnguRVOfk2GZaDpPjcOKiiXCPINZC1GczQ7iTq3Zw== + next-tick@1: version "1.0.0" resolved "https://registry.yarnpkg.com/next-tick/-/next-tick-1.0.0.tgz#ca86d1fe8828169b0120208e3dc8424b9db8342c" @@ -6789,10 +6766,6 @@ node-status-codes@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/node-status-codes/-/node-status-codes-1.0.0.tgz#5ae5541d024645d32a58fcddc9ceecea7ae3ac2f" -node-uuid@~1.4.0: - version "1.4.8" - resolved "https://registry.yarnpkg.com/node-uuid/-/node-uuid-1.4.8.tgz#b040eb0923968afabf8d32fb1f17f1167fdab907" - nodeify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/nodeify/-/nodeify-1.0.1.tgz#64ab69a7bdbaf03ce107b4f0335c87c0b9e91b1d" @@ -6913,10 +6886,6 @@ nwsapi@^2.0.7: version "2.0.8" resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.0.8.tgz#e3603579b7e162b3dbedae4fb24e46f771d8fa24" -oauth-sign@~0.6.0: - version "0.6.0" - resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.6.0.tgz#7dbeae44f6ca454e1f168451d630746735813ce3" - oauth-sign@~0.8.2: version "0.8.2" resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.8.2.tgz#46a6ab7f0aead8deae9ec0565780b7d4efeb9d43" @@ -7016,6 +6985,7 @@ open@0.0.5: optimist@^0.6.1, optimist@~0.6.0, optimist@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/optimist/-/optimist-0.6.1.tgz#da3ea74686fa21a19a111c326e90eb15a0196686" + integrity sha1-2j6nRob6IaGaERwybpDrFaAZZoY= dependencies: minimist "~0.0.1" wordwrap "~0.0.2" @@ -7069,7 +7039,7 @@ os-locale@^2.0.0: lcid "^1.0.0" mem "^1.1.0" -os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1: +os-tmpdir@^1.0.0, os-tmpdir@^1.0.1, os-tmpdir@~1.0.1, os-tmpdir@~1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/os-tmpdir/-/os-tmpdir-1.0.2.tgz#bbe67406c79aa85c5cfec766fe5734555dfa1274" @@ -7226,7 +7196,7 @@ path-is-absolute@^1.0.0, path-is-absolute@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/path-is-absolute/-/path-is-absolute-1.0.1.tgz#174b9268735534ffbc7ace6bf53a5a9e1b5c5f5f" -path-is-inside@^1.0.1: +path-is-inside@^1.0.1, path-is-inside@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/path-is-inside/-/path-is-inside-1.0.2.tgz#365417dede44430d1c11af61027facf074bdfc53" @@ -7366,6 +7336,11 @@ pluralize@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-1.2.1.tgz#d1a21483fd22bb41e58a12fa3421823140897c45" +pluralize@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/pluralize/-/pluralize-7.0.0.tgz#298b89df8b93b0221dbf421ad2b1b1ea23fc6777" + integrity sha512-ARhBOdzS3e41FbkW/XWrTEtukqqLoK5+Z/4UeDaLuSW+39JPeFgs4gCGqsrJHVZX0fUrx//4OF0K1CUGwlIFow== + pn@^1.1.0: version "1.1.0" resolved "https://registry.yarnpkg.com/pn/-/pn-1.1.0.tgz#e2f4cef0e219f463c179ab37463e4e1ecdccbafb" @@ -7624,6 +7599,13 @@ preserve@^0.2.0: version "0.2.0" resolved "https://registry.yarnpkg.com/preserve/-/preserve-0.2.0.tgz#815ed1f6ebc65926f865b310c0713bcb3315ce4b" +prettier-linter-helpers@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/prettier-linter-helpers/-/prettier-linter-helpers-1.0.0.tgz#d23d41fe1375646de2d0104d3454a3008802cf7b" + integrity sha512-GbK2cP9nraSSUF9N2XwUwqfzlAFlMNYYl+ShE/V+H8a9uNl/oUqB1w2EL54Jh0OlyRSd8RfWYJ3coVS4TROP2w== + dependencies: + fast-diff "^1.1.2" + prettier@^1.18.2: version "1.18.2" resolved "https://registry.yarnpkg.com/prettier/-/prettier-1.18.2.tgz#6823e7c5900017b4bd3acf46fe9ac4b4d7bda9ea" @@ -7682,6 +7664,11 @@ progress@^1.1.8: version "1.1.8" resolved "https://registry.yarnpkg.com/progress/-/progress-1.1.8.tgz#e260c78f6161cdd9b0e56cc3e0a85de17c7a57be" +progress@^2.0.0: + version "2.0.3" + resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8" + integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA== + promise@^7.1.1: version "7.3.1" resolved "https://registry.yarnpkg.com/promise/-/promise-7.3.1.tgz#064b72602b18f90f29192b8b1bc418ffd1ebd3bf" @@ -7762,10 +7749,6 @@ qs@6.5.1: version "6.5.1" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.1.tgz#349cdf6eef89ec45c12d7d5eb3fc0c870343a6d8" -qs@~2.4.0: - version "2.4.2" - resolved "https://registry.yarnpkg.com/qs/-/qs-2.4.2.tgz#f7ce788e5777df0b5010da7f7c4e73ba32470f5a" - qs@~6.5.1: version "6.5.2" resolved "https://registry.yarnpkg.com/qs/-/qs-6.5.2.tgz#cb3ae806e8740444584ef154ce8ee98d403f3e36" @@ -7937,6 +7920,17 @@ react-dom@^16.8.6: prop-types "^15.6.2" scheduler "^0.13.6" +react-emoji-render@^1.1.0: + version "1.1.0" + resolved "https://registry.yarnpkg.com/react-emoji-render/-/react-emoji-render-1.1.0.tgz#af494619bf1012083bc20ad18eb0a6d92d04228c" + integrity sha512-HIHIrtWd8Jel4qDgIBRQnPPFChJakuRkMFl5N5wObYjYsL7a4pkwK5P9wrEKxQWqlbviTrjGOANAfFldnVuRIQ== + dependencies: + classnames "^2.2.5" + emoji-regex "^6.4.1" + lodash.flatten "^4.4.0" + prop-types "^15.5.8" + string-replace-to-array "^1.0.1" + react-image-carousel@^2.0.18: version "2.0.18" resolved "https://registry.yarnpkg.com/react-image-carousel/-/react-image-carousel-2.0.18.tgz#5868ea09bd9cca09c4467d3d02695cd4e7792f28" @@ -8146,15 +8140,6 @@ readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable string_decoder "~1.1.1" util-deprecate "~1.0.1" -readable-stream@~1.0.26: - version "1.0.34" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.0.34.tgz#125820e34bc842d2f2aaafafe4c2916ee32c157c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - readdirp@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/readdirp/-/readdirp-2.1.0.tgz#4ed0ad060df3073300c48440373f72d1cc642d78" @@ -8303,6 +8288,11 @@ regex-not@^1.0.0, regex-not@^1.0.2: extend-shallow "^3.0.2" safe-regex "^1.1.0" +regexpp@^1.0.1: + version "1.1.0" + resolved "https://registry.yarnpkg.com/regexpp/-/regexpp-1.1.0.tgz#0e3516dd0b7904f413d2d4193dce4618c3a689ab" + integrity sha512-LOPw8FpgdQF9etWMaAfG/WRthIdXJGYp4mJ2Jgn/2lpkbod9jPn0t9UqN7AxBOKNfzRbYyVfgc7Vk4t/MpnXgw== + regexpu-core@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/regexpu-core/-/regexpu-core-2.0.0.tgz#49d038837b8dcf8bfa5b9a42139938e6ea2ae240" @@ -8384,29 +8374,6 @@ request-promise-native@^1.0.5: stealthy-require "^1.1.0" tough-cookie ">=2.3.3" -request@2.55.0: - version "2.55.0" - resolved "https://registry.yarnpkg.com/request/-/request-2.55.0.tgz#d75c1cdf679d76bb100f9bffe1fe551b5c24e93d" - dependencies: - aws-sign2 "~0.5.0" - bl "~0.9.0" - caseless "~0.9.0" - combined-stream "~0.0.5" - forever-agent "~0.6.0" - form-data "~0.2.0" - har-validator "^1.4.0" - hawk "~2.3.0" - http-signature "~0.10.0" - isstream "~0.1.1" - json-stringify-safe "~5.0.0" - mime-types "~2.0.1" - node-uuid "~1.4.0" - oauth-sign "~0.6.0" - qs "~2.4.0" - stringstream "~0.0.4" - tough-cookie ">=0.12.0" - tunnel-agent "~0.4.0" - request@^2.45.0, request@^2.79.0, request@^2.83.0, request@^2.87.0: version "2.87.0" resolved "https://registry.yarnpkg.com/request/-/request-2.87.0.tgz#32f00235cd08d482b4d0d68db93a829c0ed5756e" @@ -8444,7 +8411,7 @@ require-precompiled@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/require-precompiled/-/require-precompiled-0.1.0.tgz#5a1b52eb70ebed43eb982e974c85ab59571e56fa" -require-uncached@^1.0.2: +require-uncached@^1.0.2, require-uncached@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/require-uncached/-/require-uncached-1.0.3.tgz#4e0d56d6c9662fd31e43011c4b95aa49955421d3" dependencies: @@ -8536,6 +8503,13 @@ run-async@^0.1.0: dependencies: once "^1.3.0" +run-async@^2.2.0: + version "2.3.0" + resolved "https://registry.yarnpkg.com/run-async/-/run-async-2.3.0.tgz#0371ab4ae0bdd720d4166d7dfda64ff7a445a6c0" + integrity sha1-A3GrSuC91yDUFm19/aZP96RFpsA= + dependencies: + is-promise "^2.1.0" + run-node@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/run-node/-/run-node-1.0.0.tgz#46b50b946a2aa2d4947ae1d886e9856fd9cabe5e" @@ -8548,6 +8522,18 @@ rw@1: version "1.3.3" resolved "http://registry.npm.taobao.org/rw/download/rw-1.3.3.tgz#3f862dfa91ab766b14885ef4d01124bfda074fb4" +rx-lite-aggregates@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite-aggregates/-/rx-lite-aggregates-4.0.8.tgz#753b87a89a11c95467c4ac1626c4efc4e05c67be" + integrity sha1-dTuHqJoRyVRnxKwWJsTvxOBcZ74= + dependencies: + rx-lite "*" + +rx-lite@*, rx-lite@^4.0.8: + version "4.0.8" + resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-4.0.8.tgz#0b1e11af8bc44836f04a6407e92da42467b79444" + integrity sha1-Cx4Rr4vESDbwSmQH6S2kJGe3lEQ= + rx-lite@^3.1.2: version "3.1.2" resolved "https://registry.yarnpkg.com/rx-lite/-/rx-lite-3.1.2.tgz#19ce502ca572665f3b647b10939f97fd1615f102" @@ -8833,7 +8819,7 @@ slice-ansi@0.0.4: version "0.0.4" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35" -slice-ansi@^1.0.0: +slice-ansi@1.0.0, slice-ansi@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-1.0.0.tgz#044f1a49d8842ff307aad6b505ed178bd950134d" dependencies: @@ -8875,12 +8861,6 @@ snapdragon@^0.8.1: source-map-resolve "^0.5.0" use "^3.1.0" -sntp@1.x.x: - version "1.0.9" - resolved "https://registry.yarnpkg.com/sntp/-/sntp-1.0.9.tgz#6541184cc90aeea6c6e7b35e2659082443c66198" - dependencies: - hoek "2.x.x" - sockjs-client@^1.0.3: version "1.1.4" resolved "https://registry.yarnpkg.com/sockjs-client/-/sockjs-client-1.1.4.tgz#5babe386b775e4cf14e7520911452654016c8b12" @@ -8960,12 +8940,6 @@ source-map@0.5.6: version "0.5.6" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.6.tgz#75ce38f52bf0733c5a7f0c118d81334a2bb5f412" -source-map@^0.4.4, source-map@~0.4.1: - version "0.4.4" - resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" - dependencies: - amdefine ">=0.0.4" - source-map@^0.5.0, source-map@^0.5.3, source-map@^0.5.6, source-map@^0.5.7, source-map@~0.5.1: version "0.5.7" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.5.7.tgz#8a039d2d1021d22d1ea14c80d8ea468ba2ef3fcc" @@ -8974,6 +8948,12 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1: version "0.6.1" resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263" +source-map@~0.4.1: + version "0.4.4" + resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.4.4.tgz#eba4f5da9c0dc999de68032d8b4f76173652036b" + dependencies: + amdefine ">=0.0.4" + sourcemapped-stacktrace@^1.1.6: version "1.1.8" resolved "https://registry.yarnpkg.com/sourcemapped-stacktrace/-/sourcemapped-stacktrace-1.1.8.tgz#6b7a3f1a6fb15f6d40e701e23ce404553480d688" @@ -9137,6 +9117,15 @@ string-length@^2.0.0: astral-regex "^1.0.0" strip-ansi "^4.0.0" +string-replace-to-array@^1.0.1: + version "1.0.3" + resolved "https://registry.yarnpkg.com/string-replace-to-array/-/string-replace-to-array-1.0.3.tgz#c93eba999a5ee24d731aebbaf5aba36b5f18f7bf" + integrity sha1-yT66mZpe4k1zGuu69auja18Y978= + dependencies: + invariant "^2.2.1" + lodash.flatten "^4.2.0" + lodash.isstring "^4.0.1" + string-width@^1.0.1, string-width@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3" @@ -9145,7 +9134,7 @@ string-width@^1.0.1, string-width@^1.0.2: is-fullwidth-code-point "^1.0.0" strip-ansi "^3.0.0" -"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.1: +"string-width@^1.0.2 || 2", string-width@^2.0.0, string-width@^2.1.0, string-width@^2.1.1: version "2.1.1" resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e" dependencies: @@ -9162,10 +9151,6 @@ string_decoder@~1.1.1: dependencies: safe-buffer "~5.1.0" -stringstream@~0.0.4: - version "0.0.6" - resolved "https://registry.yarnpkg.com/stringstream/-/stringstream-0.0.6.tgz#7880225b0d4ad10e30927d167a1d6f2fd3b33a72" - strip-ansi@^0.3.0: version "0.3.0" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-0.3.0.tgz#25f48ea22ca79187f3174a4db8759347bb126220" @@ -9339,6 +9324,18 @@ symbol-tree@^3.2.1, symbol-tree@^3.2.2: version "3.2.2" resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.2.tgz#ae27db38f660a7ae2e1c3b7d1bc290819b8519e6" +table@4.0.2: + version "4.0.2" + resolved "https://registry.yarnpkg.com/table/-/table-4.0.2.tgz#a33447375391e766ad34d3486e6e2aedc84d2e36" + integrity sha512-UUkEAPdSGxtRpiV9ozJ5cMTtYiqz7Ni1OGqLXRCynrvzdtR1p+cfOWe2RJLwvUG8hNanaSRjecIqwOjqeatDsA== + dependencies: + ajv "^5.2.3" + ajv-keywords "^2.1.0" + chalk "^2.1.0" + lodash "^4.17.4" + slice-ansi "1.0.0" + string-width "^2.1.1" + table@^3.7.8: version "3.8.3" resolved "https://registry.yarnpkg.com/table/-/table-3.8.3.tgz#2bbc542f0fda9861a755d3947fefd8b3f513855f" @@ -9470,6 +9467,13 @@ tmp@0.0.28: dependencies: os-tmpdir "~1.0.1" +tmp@^0.0.33: + version "0.0.33" + resolved "https://registry.yarnpkg.com/tmp/-/tmp-0.0.33.tgz#6d34335889768d21b2bcda0aa277ced3b1bfadf9" + integrity sha512-jRCJlojKnZ3addtTOjdIqoRuPEKBvNXcGYqzO6zWZX8KfKEpnGY5jfggJQ3EjKuu8D4bJRr0y+cYJFmYbImXGw== + dependencies: + os-tmpdir "~1.0.2" + tmpl@1.0.x: version "1.0.4" resolved "https://registry.yarnpkg.com/tmpl/-/tmpl-1.0.4.tgz#23640dd7b42d00433911140820e5cf440e521dd1" @@ -9518,7 +9522,7 @@ touch@0.0.3: dependencies: nopt "~1.0.10" -tough-cookie@>=0.12.0, tough-cookie@>=2.3.3, tough-cookie@^2.3.2, tough-cookie@^2.3.3, tough-cookie@~2.3.3: +tough-cookie@>=2.3.3, tough-cookie@^2.3.2, tough-cookie@^2.3.3, tough-cookie@~2.3.3: version "2.3.4" resolved "https://registry.yarnpkg.com/tough-cookie/-/tough-cookie-2.3.4.tgz#ec60cee38ac675063ffc97a5c18970578ee83655" dependencies: @@ -9593,10 +9597,6 @@ tunnel-agent@^0.6.0: dependencies: safe-buffer "^5.0.1" -tunnel-agent@~0.4.0: - version "0.4.3" - resolved "https://registry.yarnpkg.com/tunnel-agent/-/tunnel-agent-0.4.3.tgz#6373db76909fe570e08d73583365ed828a74eeeb" - turndown-plugin-gfm@^1.0.2: version "1.0.2" resolved "https://registry.yarnpkg.com/turndown-plugin-gfm/-/turndown-plugin-gfm-1.0.2.tgz#6f8678a361f35220b2bdf5619e6049add75bf1c7" @@ -9655,14 +9655,13 @@ uc.micro@^1.0.5: resolved "https://registry.yarnpkg.com/uc.micro/-/uc.micro-1.0.6.tgz#9c411a802a409a91fc6cf74081baba34b24499ac" integrity sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA== -uglify-js@^2.6: - version "2.8.29" - resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-2.8.29.tgz#29c5733148057bb4e1f75df35b7a9cb72e6a59dd" +uglify-js@^3.1.4: + version "3.7.3" + resolved "https://registry.yarnpkg.com/uglify-js/-/uglify-js-3.7.3.tgz#f918fce9182f466d5140f24bb0ff35c2d32dcc6a" + integrity sha512-7tINm46/3puUA4hCkKYo4Xdts+JDaVC9ZPRcG8Xw9R4nhO/gZgUM3TENq8IF4Vatk8qCig4MzP/c8G4u2BkVQg== dependencies: - source-map "~0.5.1" - yargs "~3.10.0" - optionalDependencies: - uglify-to-browserify "~1.0.0" + commander "~2.20.3" + source-map "~0.6.1" uglify-js@^3.5.1: version "3.6.9" @@ -10098,10 +10097,6 @@ window-size@0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.0.tgz#5438cd2ea93b202efa3a19fe8887aee7c94f9c9d" -window-size@^0.1.4: - version "0.1.4" - resolved "https://registry.yarnpkg.com/window-size/-/window-size-0.1.4.tgz#f8e1aa1ee5a53ec5bf151ffa09742a6ad7697876" - window@4.2.5: version "4.2.5" resolved "https://registry.yarnpkg.com/window/-/window-4.2.5.tgz#02b5c48daf462481d5dfc6d331fbfa4d27d78ee4" @@ -10119,6 +10114,7 @@ wordwrap@0.0.2: wordwrap@~0.0.2: version "0.0.3" resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107" + integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc= wordwrap@~1.0.0: version "1.0.0" @@ -10231,7 +10227,7 @@ xtend@~2.1.1: dependencies: object-keys "~0.4.0" -y18n@^3.2.0, y18n@^3.2.1: +y18n@^3.2.1: version "3.2.1" resolved "https://registry.yarnpkg.com/y18n/-/y18n-3.2.1.tgz#6d15fba884c08679c0d77e88e7759e811e07fa41" @@ -10255,6 +10251,13 @@ yargs-parser@^4.2.0: dependencies: camelcase "^3.0.0" +yargs-parser@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-5.0.0.tgz#275ecf0d7ffe05c77e64e7c86e4cd94bf0e1228a" + integrity sha1-J17PDX/+Bcd+ZOfIbkzZS/DhIoo= + dependencies: + camelcase "^3.0.0" + yargs-parser@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/yargs-parser/-/yargs-parser-8.1.0.tgz#f1376a33b6629a5d063782944da732631e966950" @@ -10278,18 +10281,6 @@ yargs@^10.0.3: y18n "^3.2.1" yargs-parser "^8.1.0" -yargs@^3.32.0: - version "3.32.0" - resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.32.0.tgz#03088e9ebf9e756b69751611d2a5ef591482c995" - dependencies: - camelcase "^2.0.1" - cliui "^3.0.3" - decamelize "^1.1.1" - os-locale "^1.4.0" - string-width "^1.0.1" - window-size "^0.1.4" - y18n "^3.2.0" - yargs@^6.0.0: version "6.6.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-6.6.0.tgz#782ec21ef403345f830a808ca3d513af56065208" @@ -10308,6 +10299,25 @@ yargs@^6.0.0: y18n "^3.2.1" yargs-parser "^4.2.0" +yargs@^7.0.2: + version "7.1.0" + resolved "https://registry.yarnpkg.com/yargs/-/yargs-7.1.0.tgz#6ba318eb16961727f5d284f8ea003e8d6154d0c8" + integrity sha1-a6MY6xaWFyf10oT46gA+jWFU0Mg= + dependencies: + camelcase "^3.0.0" + cliui "^3.2.0" + decamelize "^1.1.1" + get-caller-file "^1.0.1" + os-locale "^1.4.0" + read-pkg-up "^1.0.1" + require-directory "^2.1.1" + require-main-filename "^1.0.1" + set-blocking "^2.0.0" + string-width "^1.0.2" + which-module "^1.0.0" + y18n "^3.2.1" + yargs-parser "^5.0.0" + yargs@~3.10.0: version "3.10.0" resolved "https://registry.yarnpkg.com/yargs/-/yargs-3.10.0.tgz#f7ee7bd857dd7c1d2d38c0e74efbd681d1431fd1"