diff --git a/browser/components/MarkdownPreview.js b/browser/components/MarkdownPreview.js index bef6a976..6643d796 100755 --- a/browser/components/MarkdownPreview.js +++ b/browser/components/MarkdownPreview.js @@ -16,7 +16,6 @@ import convertModeName from 'browser/lib/convertModeName' import copy from 'copy-to-clipboard' import mdurl from 'mdurl' import exportNote from 'browser/main/lib/dataApi/exportNote' -import { escapeHtmlCharacters } from 'browser/lib/utils' import context from 'browser/lib/context' import i18n from 'browser/lib/i18n' import fs from 'fs' @@ -331,9 +330,7 @@ export default class MarkdownPreview extends React.Component { allowCustomCSS, customCSS ) - let body = this.markdown.render( - escapeHtmlCharacters(noteContent, { detectCodeBlock: true }) - ) + let body = this.markdown.render(noteContent) const files = [this.GetCodeThemeLink(codeBlockTheme), ...CSS_FILES] const attachmentsAbsolutePaths = attachmentManagement.getAbsolutePathsOfAttachmentsInContent( noteContent, diff --git a/browser/components/NoteItem.js b/browser/components/NoteItem.js index 600b7e2d..2fc70a39 100644 --- a/browser/components/NoteItem.js +++ b/browser/components/NoteItem.js @@ -24,16 +24,19 @@ const TagElement = ({ tagName }) => ( /** * @description Tag element list component. * @param {Array|null} tags + * @param {boolean} showTagsAlphabetically * @return {React.Component} */ -const TagElementList = tags => { +const TagElementList = (tags, showTagsAlphabetically) => { if (!isArray(tags)) { return [] } - const tagElements = tags.map(tag => TagElement({ tagName: tag })) - - return tagElements + if (showTagsAlphabetically) { + return _.sortBy(tags).map(tag => TagElement({ tagName: tag })) + } else { + return tags.map(tag => TagElement({ tagName: tag })) + } } /** @@ -55,7 +58,8 @@ const NoteItem = ({ pathname, storageName, folderName, - viewType + viewType, + showTagsAlphabetically }) => (
{note.tags.length > 0 - ? TagElementList(note.tags) + ? TagElementList(note.tags, showTagsAlphabetically) : (
{storageList.length > 0 ? storageList : ( -
No storage mount.
+
No storage mount.
)}
) StorageList.propTypes = { - storgaeList: PropTypes.arrayOf(PropTypes.element).isRequired + storageList: PropTypes.arrayOf(PropTypes.element).isRequired } export default CSSModules(StorageList, styles) diff --git a/browser/components/TagListItem.js b/browser/components/TagListItem.js index 19f11791..eec8ab14 100644 --- a/browser/components/TagListItem.js +++ b/browser/components/TagListItem.js @@ -25,7 +25,7 @@ const TagListItem = ({name, handleClickTagListItem, handleClickNarrowToTag, hand
diff --git a/browser/lib/Languages.js b/browser/lib/Languages.js index 087210be..1a798fdf 100644 --- a/browser/lib/Languages.js +++ b/browser/lib/Languages.js @@ -61,6 +61,9 @@ const languages = [ }, { name: 'Turkish', locale: 'tr' + }, { + name: 'Thai', + locale: 'th' } ] diff --git a/browser/lib/markdown-it-sanitize-html.js b/browser/lib/markdown-it-sanitize-html.js index 05e5e7be..bd3d452e 100644 --- a/browser/lib/markdown-it-sanitize-html.js +++ b/browser/lib/markdown-it-sanitize-html.js @@ -2,6 +2,7 @@ import sanitizeHtml from 'sanitize-html' import { escapeHtmlCharacters } from './utils' +import url from 'url' module.exports = function sanitizePlugin (md, options) { options = options || {} @@ -25,7 +26,7 @@ module.exports = function sanitizePlugin (md, options) { const inlineTokens = state.tokens[tokenIdx].children for (let childIdx = 0; childIdx < inlineTokens.length; childIdx++) { if (inlineTokens[childIdx].type === 'html_inline') { - inlineTokens[childIdx].content = sanitizeHtml( + inlineTokens[childIdx].content = sanitizeInline( inlineTokens[childIdx].content, options ) @@ -35,3 +36,89 @@ module.exports = function sanitizePlugin (md, options) { } }) } + +const tagRegex = /<([A-Z][A-Z0-9]*)\s*((?:\s*[A-Z][A-Z0-9]*(?:=("|')(?:[^\3]+?)\3)?)*)\s*\/?>|<\/([A-Z][A-Z0-9]*)\s*>/i +const attributesRegex = /([A-Z][A-Z0-9]*)(?:=("|')([^\2]+?)\2)?/ig + +function sanitizeInline (html, options) { + let match = tagRegex.exec(html) + if (!match) { + return '' + } + + const { allowedTags, allowedAttributes, selfClosing, allowedSchemesAppliedToAttributes } = options + + if (match[1] !== undefined) { + // opening tag + const tag = match[1].toLowerCase() + if (allowedTags.indexOf(tag) === -1) { + return '' + } + + const attributes = match[2] + + let attrs = '' + let name + let value + + while ((match = attributesRegex.exec(attributes))) { + name = match[1].toLowerCase() + value = match[3] + + if (allowedAttributes['*'].indexOf(name) !== -1 || (allowedAttributes[tag] && allowedAttributes[tag].indexOf(name) !== -1)) { + if (allowedSchemesAppliedToAttributes.indexOf(name) !== -1) { + if (naughtyHRef(value, options) || (tag === 'iframe' && name === 'src' && naughtyIFrame(value, options))) { + continue + } + } + + attrs += ` ${name}` + if (match[2]) { + attrs += `="${value}"` + } + } + } + + if (selfClosing.indexOf(tag) === -1) { + return '<' + tag + attrs + '>' + } else { + return '<' + tag + attrs + ' />' + } + } else { + // closing tag + if (allowedTags.indexOf(match[4].toLowerCase()) !== -1) { + return html + } else { + return '' + } + } +} + +function naughtyHRef (href, options) { + // href = href.replace(/[\x00-\x20]+/g, '') + href = href.replace(/<\!\-\-.*?\-\-\>/g, '') + + const matches = href.match(/^([a-zA-Z]+)\:/) + if (!matches) { + if (href.match(/^[\/\\]{2}/)) { + return !options.allowProtocolRelative + } + + // No scheme + return false + } + + const scheme = matches[1].toLowerCase() + + return options.allowedSchemes.indexOf(scheme) === -1 +} + +function naughtyIFrame (src, options) { + try { + const parsed = url.parse(src, false, true) + + return options.allowedIframeHostnames.index(parsed.hostname) === -1 + } catch (e) { + return true + } +} diff --git a/browser/lib/markdown.js b/browser/lib/markdown.js index 49260740..7fbb5a38 100644 --- a/browser/lib/markdown.js +++ b/browser/lib/markdown.js @@ -106,7 +106,11 @@ class Markdown { 'iframe': ['src', 'width', 'height', 'frameborder', 'allowfullscreen'], 'input': ['type', 'id', 'checked'] }, - allowedIframeHostnames: ['www.youtube.com'] + allowedIframeHostnames: ['www.youtube.com'], + selfClosing: [ 'img', 'br', 'hr', 'input' ], + allowedSchemes: [ 'http', 'https', 'ftp', 'mailto' ], + allowedSchemesAppliedToAttributes: [ 'href', 'src', 'cite' ], + allowProtocolRelative: true }) } diff --git a/browser/main/Detail/MarkdownNoteDetail.js b/browser/main/Detail/MarkdownNoteDetail.js index c35127ba..116fdec0 100755 --- a/browser/main/Detail/MarkdownNoteDetail.js +++ b/browser/main/Detail/MarkdownNoteDetail.js @@ -348,7 +348,7 @@ class MarkdownNoteDetail extends React.Component { } render () { - const { data, location } = this.props + const { data, location, config } = this.props const { note, editorType } = this.state const storageKey = note.storage const folderKey = note.folder @@ -399,6 +399,8 @@ class MarkdownNoteDetail extends React.Component { diff --git a/browser/main/Detail/SnippetNoteDetail.js b/browser/main/Detail/SnippetNoteDetail.js index 65d5dfd3..afd81102 100644 --- a/browser/main/Detail/SnippetNoteDetail.js +++ b/browser/main/Detail/SnippetNoteDetail.js @@ -354,12 +354,10 @@ class SnippetNoteDetail extends React.Component { this.refs['code-' + this.state.snippetIndex].reload() if (this.visibleTabs.offsetWidth > this.allTabs.scrollWidth) { - console.log('no need for arrows') this.moveTabBarBy(0) } else { const lastTab = this.allTabs.lastChild if (lastTab.offsetLeft + lastTab.offsetWidth < this.visibleTabs.offsetWidth) { - console.log('need to scroll') const width = this.visibleTabs.offsetWidth const newLeft = lastTab.offsetLeft + lastTab.offsetWidth - width this.moveTabBarBy(newLeft > 0 ? -newLeft : 0) @@ -627,7 +625,6 @@ class SnippetNoteDetail extends React.Component { } focusEditor () { - console.log('code-' + this.state.snippetIndex) this.refs['code-' + this.state.snippetIndex].focus() } @@ -759,6 +756,8 @@ class SnippetNoteDetail extends React.Component { this.handleChange(e)} /> diff --git a/browser/main/Detail/TagSelect.js b/browser/main/Detail/TagSelect.js index eb160e4c..6ced475b 100644 --- a/browser/main/Detail/TagSelect.js +++ b/browser/main/Detail/TagSelect.js @@ -179,10 +179,10 @@ class TagSelect extends React.Component { } render () { - const { value, className } = this.props + const { value, className, showTagsAlphabetically } = this.props const tagList = _.isArray(value) - ? value.map((tag) => { + ? (showTagsAlphabetically ? _.sortBy(value) : value).map((tag) => { return ( { - console.log(data) store.dispatch({ type: 'ADD_STORAGE', storage: data.storage, diff --git a/browser/main/NoteList/index.js b/browser/main/NoteList/index.js index 30ad93c3..13117af1 100644 --- a/browser/main/NoteList/index.js +++ b/browser/main/NoteList/index.js @@ -56,7 +56,6 @@ class NoteList extends React.Component { super(props) this.selectNextNoteHandler = () => { - console.log('fired next') this.selectNextNote() } this.selectPriorNoteHandler = () => { @@ -616,7 +615,6 @@ class NoteList extends React.Component { .catch((err) => { console.error('Cannot Delete note: ' + err) }) - console.log('Notes were all deleted') } else { if (!confirmDeleteNote(confirmDeletion, false)) return @@ -636,7 +634,6 @@ class NoteList extends React.Component { }) }) AwsMobileAnalyticsConfig.recordDynamicCustomEvent('EDIT_NOTE') - console.log('Notes went to trash') }) .catch((err) => { console.error('Notes could not go to trash: ' + err) @@ -996,6 +993,7 @@ class NoteList extends React.Component { folderName={this.getNoteFolder(note).name} storageName={this.getNoteStorage(note).name} viewType={viewType} + showTagsAlphabetically={config.ui.showTagsAlphabetically} /> ) } diff --git a/browser/main/SideNav/index.js b/browser/main/SideNav/index.js index e78c9c77..3e18095e 100644 --- a/browser/main/SideNav/index.js +++ b/browser/main/SideNav/index.js @@ -20,6 +20,10 @@ import i18n from 'browser/lib/i18n' import context from 'browser/lib/context' import { remote } from 'electron' +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 @@ -202,12 +206,20 @@ class SideNav extends React.Component { tagListComponent () { const { data, location, config } = this.props - const relatedTags = this.getRelatedTags(this.getActiveTags(location.pathname), data.noteMap) + 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) }) - ), ['name']).filter( + ).filter( tag => tag.size > 0 - ) + ), ['name']) + 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 + return tag + }) + } if (config.sortTagsBy === 'COUNTER') { tagList = _.sortBy(tagList, item => (0 - item.size)) } @@ -306,7 +318,6 @@ class SideNav extends React.Component { .catch((err) => { console.error('Cannot Delete note: ' + err) }) - console.log('Trash emptied') } handleFilterButtonContextMenu (event) { diff --git a/browser/main/lib/AwsMobileAnalyticsConfig.js b/browser/main/lib/AwsMobileAnalyticsConfig.js index 1ef4f8da..e4a21a92 100644 --- a/browser/main/lib/AwsMobileAnalyticsConfig.js +++ b/browser/main/lib/AwsMobileAnalyticsConfig.js @@ -45,7 +45,6 @@ function initAwsMobileAnalytics () { if (getSendEventCond()) return AWS.config.credentials.get((err) => { if (!err) { - console.log('Cognito Identity ID: ' + AWS.config.credentials.identityId) recordDynamicCustomEvent('APP_STARTED') recordStaticCustomEvent() } @@ -58,7 +57,7 @@ function recordDynamicCustomEvent (type, options = {}) { mobileAnalyticsClient.recordEvent(type, options) } catch (analyticsError) { if (analyticsError instanceof ReferenceError) { - console.log(analyticsError.name + ': ' + analyticsError.message) + console.error(analyticsError.name + ': ' + analyticsError.message) } } } @@ -71,7 +70,7 @@ function recordStaticCustomEvent () { }) } catch (analyticsError) { if (analyticsError instanceof ReferenceError) { - console.log(analyticsError.name + ': ' + analyticsError.message) + console.error(analyticsError.name + ': ' + analyticsError.message) } } } diff --git a/browser/main/lib/dataApi/attachmentManagement.js b/browser/main/lib/dataApi/attachmentManagement.js index 912450c1..c193eaf2 100644 --- a/browser/main/lib/dataApi/attachmentManagement.js +++ b/browser/main/lib/dataApi/attachmentManagement.js @@ -529,7 +529,6 @@ function handleAttachmentLinkPaste (storageKey, noteKey, linkText) { return modifiedLinkText }) } else { - console.log('One if the parameters was null -> Do nothing..') return Promise.resolve(linkText) } } diff --git a/browser/main/lib/dataApi/renameStorage.js b/browser/main/lib/dataApi/renameStorage.js index 78242bed..3b806d1c 100644 --- a/browser/main/lib/dataApi/renameStorage.js +++ b/browser/main/lib/dataApi/renameStorage.js @@ -14,7 +14,6 @@ function renameStorage (key, name) { cachedStorageList = JSON.parse(localStorage.getItem('storages')) if (!_.isArray(cachedStorageList)) throw new Error('invalid storages') } catch (err) { - console.log('error got') console.error(err) return Promise.reject(err) } diff --git a/browser/main/lib/dataApi/resolveStorageData.js b/browser/main/lib/dataApi/resolveStorageData.js index 681a102e..da41f3d0 100644 --- a/browser/main/lib/dataApi/resolveStorageData.js +++ b/browser/main/lib/dataApi/resolveStorageData.js @@ -31,13 +31,9 @@ function resolveStorageData (storageCache) { const version = parseInt(storage.version, 10) if (version >= 1) { - if (version > 1) { - console.log('The repository version is newer than one of current app.') - } return Promise.resolve(storage) } - console.log('Transform Legacy storage', storage.path) return migrateFromV6Storage(storage.path) .then(() => storage) } diff --git a/browser/main/lib/dataApi/resolveStorageNotes.js b/browser/main/lib/dataApi/resolveStorageNotes.js index fa3f19ae..9da27248 100644 --- a/browser/main/lib/dataApi/resolveStorageNotes.js +++ b/browser/main/lib/dataApi/resolveStorageNotes.js @@ -9,7 +9,7 @@ function resolveStorageNotes (storage) { notePathList = sander.readdirSync(notesDirPath) } catch (err) { if (err.code === 'ENOENT') { - console.log(notesDirPath, ' doesn\'t exist.') + console.error(notesDirPath, ' doesn\'t exist.') sander.mkdirSync(notesDirPath) } else { console.warn('Failed to find note dir', notesDirPath, err) diff --git a/browser/main/lib/dataApi/toggleStorage.js b/browser/main/lib/dataApi/toggleStorage.js index dbb625c3..246d85ef 100644 --- a/browser/main/lib/dataApi/toggleStorage.js +++ b/browser/main/lib/dataApi/toggleStorage.js @@ -12,7 +12,6 @@ function toggleStorage (key, isOpen) { cachedStorageList = JSON.parse(localStorage.getItem('storages')) if (!_.isArray(cachedStorageList)) throw new Error('invalid storages') } catch (err) { - console.log('error got') console.error(err) return Promise.reject(err) } diff --git a/browser/main/lib/eventEmitter.js b/browser/main/lib/eventEmitter.js index de08f078..1276545b 100644 --- a/browser/main/lib/eventEmitter.js +++ b/browser/main/lib/eventEmitter.js @@ -14,7 +14,6 @@ function once (name, listener) { } function emit (name, ...args) { - console.log(name) remote.getCurrentWindow().webContents.send(name, ...args) } diff --git a/browser/main/lib/ipcClient.js b/browser/main/lib/ipcClient.js index 0c916617..c06296b5 100644 --- a/browser/main/lib/ipcClient.js +++ b/browser/main/lib/ipcClient.js @@ -14,14 +14,13 @@ nodeIpc.connectTo( path.join(app.getPath('userData'), 'boostnote.service'), function () { nodeIpc.of.node.on('error', function (err) { - console.log(err) + console.error(err) }) nodeIpc.of.node.on('connect', function () { - console.log('Connected successfully') ipcRenderer.send('config-renew', {config: ConfigManager.get()}) }) nodeIpc.of.node.on('disconnect', function () { - console.log('disconnected') + return }) } ) diff --git a/browser/main/modals/PreferencesModal/HotkeyTab.js b/browser/main/modals/PreferencesModal/HotkeyTab.js index 9f964957..7ad6f606 100644 --- a/browser/main/modals/PreferencesModal/HotkeyTab.js +++ b/browser/main/modals/PreferencesModal/HotkeyTab.js @@ -28,10 +28,20 @@ class HotkeyTab extends React.Component { }}) } this.handleSettingError = (err) => { - this.setState({keymapAlert: { - type: 'error', - message: err.message != null ? err.message : i18n.__('An error occurred!') - }}) + if ( + this.state.config.hotkey.toggleMain === '' || + this.state.config.hotkey.toggleMode === '' + ) { + 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.oldHotkey = this.state.config.hotkey ipc.addListener('APP_SETTING_DONE', this.handleSettingDone) diff --git a/browser/main/modals/PreferencesModal/UiTab.js b/browser/main/modals/PreferencesModal/UiTab.js index 6a9abe65..6bc3b0a3 100644 --- a/browser/main/modals/PreferencesModal/UiTab.js +++ b/browser/main/modals/PreferencesModal/UiTab.js @@ -71,6 +71,9 @@ class UiTab extends React.Component { 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, disableDirectWrite: this.refs.uiD2w != null ? this.refs.uiD2w.checked : false @@ -249,16 +252,6 @@ class UiTab extends React.Component { {i18n.__('Show a confirmation dialog when deleting notes')}
-
- -
{ global.process.platform === 'win32' ?
@@ -274,6 +267,52 @@ class UiTab extends React.Component {
: null } +
Tags
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+ +
+
Editor
diff --git a/browser/main/store.js b/browser/main/store.js index b8f13cc8..11ff2f3f 100644 --- a/browser/main/store.js +++ b/browser/main/store.js @@ -113,7 +113,6 @@ function data (state = defaultDataMap(), action) { // If storage chanced, origin key must be discarded if (originKey !== uniqueKey) { - console.log('diffrent storage') // From isStarred if (originNote.isStarred) { state.starredSet = new Set(state.starredSet) diff --git a/dev-scripts/dev.js b/dev-scripts/dev.js index 000a1bfd..9698a2fe 100644 --- a/dev-scripts/dev.js +++ b/dev-scripts/dev.js @@ -49,7 +49,7 @@ function startServer () { } function startElectron () { - spawn(electron, ['--hot', './index.js']) + spawn(electron, ['--hot', './index.js'], { stdio: 'inherit' }) .on('close', () => { server.close() }) diff --git a/lib/main-app.js b/lib/main-app.js index 1ab9f4ca..f25d07d2 100644 --- a/lib/main-app.js +++ b/lib/main-app.js @@ -59,7 +59,7 @@ updater.on('update-downloaded', (info) => { }) updater.autoUpdater.on('error', (err) => { - console.log(err) + console.error(err) }) ipc.on('update-app-confirm', function (event, msg) { diff --git a/lib/main-window.js b/lib/main-window.js index fa54d5ce..512782de 100644 --- a/lib/main-window.js +++ b/lib/main-window.js @@ -7,13 +7,19 @@ const config = new Config() const _ = require('lodash') var showMenu = process.platform !== 'win32' -const windowSize = config.get('windowsize') || { x: null, y: null, width: 1080, height: 720 } +const windowSize = config.get('windowsize') || { + x: null, + y: null, + width: 1080, + height: 720 +} const mainWindow = new BrowserWindow({ x: windowSize.x, y: windowSize.y, width: windowSize.width, height: windowSize.height, + useContentSize: true, minWidth: 500, minHeight: 320, autoHideMenuBar: showMenu, diff --git a/locales/de.json b/locales/de.json index c2465a22..1b90ab63 100644 --- a/locales/de.json +++ b/locales/de.json @@ -145,6 +145,7 @@ "UserName": "Benutzername", "Password": "Passwort", "Russian": "Russisch", + "Thai": "Thai (ภาษาไทย)", "Command(⌘)": "Befehlstaste(⌘)", "Editor Rulers": "Editor Trennline", "Enable": "Aktiviert", diff --git a/locales/en.json b/locales/en.json index 6ccbb563..a20ab7b0 100644 --- a/locales/en.json +++ b/locales/en.json @@ -156,6 +156,7 @@ "Password": "Password", "Russian": "Russian", "Hungarian": "Hungarian", + "Thai": "Thai (ภาษาไทย)", "Command(⌘)": "Command(⌘)", "Add Storage": "Add Storage", "Name": "Name", @@ -178,6 +179,8 @@ "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.", "⚠ 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! ⚠", "Disabled": "Disabled", + "Save tags of a note in alphabetical order": "Save tags of a note in alphabetical order", + "Enable live count of notes": "Enable live count of notes", "Enable smart table editor": "Enable smart table editor", "Snippet Default Language": "Snippet Default Language" } diff --git a/locales/es-ES.json b/locales/es-ES.json index d188029d..8b2da1b7 100644 --- a/locales/es-ES.json +++ b/locales/es-ES.json @@ -143,6 +143,7 @@ "Spanish": "Español", "Unsaved Changes!": "¡Tienes que guardar!", "Russian": "Ruso", + "Thai": "Thai (ภาษาไทย)", "Command(⌘)": "Comando(⌘)", "Editor Rulers": "Reglas del editor", "Enable": "Activar", diff --git a/locales/fa.json b/locales/fa.json index cb8c5671..18bef679 100644 --- a/locales/fa.json +++ b/locales/fa.json @@ -146,6 +146,7 @@ "UserName": "نام کاربری", "Password": "رمز عبور", "Russian": "روسی", + "Thai": "Thai (ภาษาไทย)", "Command(⌘)": "Command(⌘)", "Editor Rulers": "Editor Rulers", "Enable": "فعال", diff --git a/locales/fr.json b/locales/fr.json index bea9b647..bf2b61b9 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -142,6 +142,7 @@ "Spanish": "Espagnol", "Unsaved Changes!": "Il faut sauvegarder !", "Russian": "Russe", + "Thai": "Thai (ภาษาไทย)", "Command(⌘)": "Command(⌘)", "Editor Rulers": "Règles dans l'éditeur", "Enable": "Activer", @@ -153,6 +154,9 @@ "Allow dangerous html tags": "Accepter les tags html dangereux", "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "Convertir des flèches textuelles en jolis signes. ⚠ Cela va interferérer avec les éventuels commentaires HTML dans votre Markdown.", "⚠ 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! ⚠": "⚠ Vous avez collé un lien qui référence une pièce-jointe qui n'a pas pu être récupéré dans le dossier de stockage de la note. Coller des liens qui font référence à des pièces-jointes ne fonctionne que si la source et la destination et la même. Veuillez plutôt utiliser du Drag & Drop ! ⚠", + "Save tags of a note in alphabetical order": "Sauvegarder les tags d'une note en ordre alphabétique", + "Show tags of a note in alphabetical order": "Afficher les tags d'une note par ordre alphabétique", + "Enable live count of notes": "Activer le comptage live des notes", "Enable smart table editor": "Activer l'intelligent éditeur de tableaux", "Snippet Default Language": "Langage par défaut d'un snippet", "Disabled": "Disabled", diff --git a/locales/hu.json b/locales/hu.json index b6fe3222..77bdb2ab 100644 --- a/locales/hu.json +++ b/locales/hu.json @@ -155,6 +155,7 @@ "Password": "Jelszo", "Russian": "Russian", "Hungarian": "Hungarian", + "Thai": "Thai (ภาษาไทย)", "Command(⌘)": "Command(⌘)", "Add Storage": "Tároló hozzáadása", "Name": "Név", diff --git a/locales/it.json b/locales/it.json index 85e5086e..05f454f3 100644 --- a/locales/it.json +++ b/locales/it.json @@ -146,6 +146,7 @@ "UserName": "UserName", "Password": "Password", "Russian": "Russo", + "Thai": "Thai (ภาษาไทย)", "Command(⌘)": "Comando(⌘)", "Editor Rulers": "Regole dell'editor", "Enable": "Abilita", diff --git a/locales/ja.json b/locales/ja.json index 8a8b3d4d..e33bbaa6 100644 --- a/locales/ja.json +++ b/locales/ja.json @@ -155,6 +155,7 @@ "Password": "パスワード", "Russian": "ロシア語", "Hungarian": "ハンガリー語", + "Thai": "Thai (ภาษาไทย)", "Command(⌘)": "コマンド(⌘)", "Add Storage": "ストレージを追加", "Name": "名前", diff --git a/locales/ko.json b/locales/ko.json index 72b7c43c..9a8bf8c7 100644 --- a/locales/ko.json +++ b/locales/ko.json @@ -143,6 +143,7 @@ "Spanish": "Spanish", "Unsaved Changes!": "저장해주세요!", "Russian": "Russian", + "Thai": "Thai (ภาษาไทย)", "Command(⌘)": "Command(⌘)", "Delete Folder": "폴더 삭제", "This will delete all notes in the folder and can not be undone.": "폴더의 모든 노트를 지우게 되고, 되돌릴 수 없습니다.", diff --git a/locales/no.json b/locales/no.json index 19eee8a4..2d6c92f5 100644 --- a/locales/no.json +++ b/locales/no.json @@ -143,6 +143,7 @@ "Spanish": "Spanish", "Unsaved Changes!": "Unsaved Changes!", "Russian": "Russian", + "Thai": "Thai (ภาษาไทย)", "Editor Rulers": "Editor Rulers", "Enable": "Enable", "Disable": "Disable", diff --git a/locales/pl.json b/locales/pl.json index 26b420ab..68719aef 100644 --- a/locales/pl.json +++ b/locales/pl.json @@ -149,6 +149,7 @@ "Spanish": "Hiszpański", "Unsaved Changes!": "Musisz zapisać!", "Russian": "Rosyjski", + "Thai": "Thai (ภาษาไทย)", "Editor Rulers": "Margines", "Enable": "Włącz", "Disable": "Wyłącz", diff --git a/locales/pt-BR.json b/locales/pt-BR.json index a5af23ee..6b3126cc 100644 --- a/locales/pt-BR.json +++ b/locales/pt-BR.json @@ -143,6 +143,7 @@ "Spanish": "Espanhol", "Unsaved Changes!": "Você precisa salvar!", "Russian": "Russo", + "Thai": "Thai (ภาษาไทย)", "Editor Rulers": "Réguas do Editor", "Enable": "Habilitado", "Disable": "Desabilitado", diff --git a/locales/pt-PT.json b/locales/pt-PT.json index 19eee8a4..2d6c92f5 100644 --- a/locales/pt-PT.json +++ b/locales/pt-PT.json @@ -143,6 +143,7 @@ "Spanish": "Spanish", "Unsaved Changes!": "Unsaved Changes!", "Russian": "Russian", + "Thai": "Thai (ภาษาไทย)", "Editor Rulers": "Editor Rulers", "Enable": "Enable", "Disable": "Disable", diff --git a/locales/ru.json b/locales/ru.json index 90aa8032..793e1511 100644 --- a/locales/ru.json +++ b/locales/ru.json @@ -144,6 +144,7 @@ "UserName": "Имя пользователя", "Password": "Пароль", "Russian": "Русский", + "Thai": "Thai (ภาษาไทย)", "Editor Rulers": "Editor Rulers", "Enable": "Enable", "Disable": "Disable", diff --git a/locales/sq.json b/locales/sq.json index 15d1a34f..e4cc01ac 100644 --- a/locales/sq.json +++ b/locales/sq.json @@ -142,6 +142,7 @@ "Spanish": "Spanish", "Unsaved Changes!": "Unsaved Changes!", "Russian": "Russian", + "Thai": "Thai (ภาษาไทย)", "Editor Rulers": "Editor Rulers", "Enable": "Enable", "Disable": "Disable", diff --git a/locales/th.json b/locales/th.json new file mode 100644 index 00000000..49d8e7cd --- /dev/null +++ b/locales/th.json @@ -0,0 +1,182 @@ +{ + "Notes": "โน๊ต", + "Tags": "แท็ก", + "Preferences": "ตั้งค่า", + "Make a note": "สร้างโน๊ต", + "Ctrl": "Ctrl", + "Ctrl(^)": "Ctrl(^)", + "to create a new note": "เพื่อสร้างโน๊ต", + "Toggle Mode": "Toggle Mode", + "Add tag...": "เพิ่มแท็ก...", + "Trash": "ถังขยะ", + "MODIFICATION DATE": "แก้ไขเมื่อ", + "Words": "คำ", + "Letters": "ตัวอักษร", + "STORAGE": "แหล่งจัดเก็บ", + "FOLDER": "โฟลเดอร์", + "CREATION DATE": "สร้างเมื่อ", + "NOTE LINK": "NOTE LINK", + ".md": ".md", + ".txt": ".txt", + ".html": ".html", + "Print": "พิมพ์", + "Your preferences for Boostnote": "การตั้งค่าของคุณสำหรับ Boostnote", + "Help": "ช่วยเหลือ", + "Hide Help": "ซ่อนการช่วยเหลือ", + "Storages": "แหล่งจัดเก็บ", + "Add Storage Location": "เพิ่มแหล่งจัดเก็บ", + "Add Folder": "เพิ่มโฟลเดอร์", + "Select Folder": "เลือกโฟลเดอร์", + "Open Storage folder": "เปิดโฟลเดอร์แหล่งจัดเก็บ", + "Unlink": "ยกเลิกการลิงค์", + "Edit": "แก้ไข", + "Delete": "ลบ", + "Interface": "หน้าตาโปรแกรม", + "Interface Theme": "ธีมของโปรแกรม", + "Default": "ค่าเริ่มต้น", + "White": "โทนสว่าง", + "Solarized Dark": "Solarized Dark", + "Dark": "โทนมืด", + "Show a confirmation dialog when deleting notes": "แสดงหน้าต่างยืนยันเมื่อทำการลบโน๊ต", + "Disable Direct Write (It will be applied after restarting)": "ปิด Direct Write (It will be applied after restarting)", + "Show only related tags": "แสดงเฉพาะแท็กที่เกี่ยวข้อง", + "Editor Theme": "ธีมของ Editor", + "Editor Font Size": "ขนาดอักษรของ Editor", + "Editor Font Family": "แบบอักษรของ Editor", + "Editor Indent Style": "รูปแบบการย่อหน้าของ Editor", + "Spaces": "ช่องว่าง", + "Tabs": "แท็บ", + "Switch to Preview": "Switch to Preview", + "When Editor Blurred": "When Editor Blurred", + "When Editor Blurred, Edit On Double Click": "When Editor Blurred, Edit On Double Click", + "On Right Click": "On Right Click", + "Editor Keymap": "รูปแบบคีย์ลัดของ Editor", + "default": "ค่าเริ่มต้น", + "vim": "vim", + "emacs": "emacs", + "⚠️ Please restart boostnote after you change the keymap": "⚠️ กรุณาปิดและเปิดโปรแกรมใหม่ หลังจากคุณเปลี่ยนคีย์ลัด", + "Show line numbers in the editor": "แสดงหมายเลขบรรทัด", + "Allow editor to scroll past the last line": "อนุญาตให้เลื่อน Scroll เลยบรรทัดสุดท้ายได้", + "Enable smart quotes": "เปิด Smart quotes", + "Bring in web page title when pasting URL on editor": "แสดงชื่อ Title ของเว็บไซต์เมื่อวางลิงค์ใน Editor", + "Preview": "พรีวิว", + "Preview Font Size": "ขนาดอักษร", + "Preview Font Family": "แบบอักษร", + "Code block Theme": "ธีมของ Code block", + "Allow preview to scroll past the last line": "อนุญาตให้เลื่อน Scroll เลยบรรทัดสุดท้ายได้", + "Show line numbers for preview code blocks": "แสดงหมายเลขบรรทัดใน Code block", + "LaTeX Inline Open Delimiter": "LaTeX Inline Open Delimiter", + "LaTeX Inline Close Delimiter": "LaTeX Inline Close Delimiter", + "LaTeX Block Open Delimiter": "LaTeX Block Open Delimiter", + "LaTeX Block Close Delimiter": "LaTeX Block Close Delimiter", + "PlantUML Server": "เซิฟเวอร์ของ PlantUML", + "Community": "ชุมชนผู้ใช้", + "Subscribe to Newsletter": "สมัครรับข่าวสาร", + "GitHub": "GitHub", + "Blog": "บล็อก", + "Facebook Group": "กลุ่ม Facebook", + "Twitter": "Twitter", + "About": "เกี่ยวกับ", + "Boostnote": "Boostnote", + "An open source note-taking app made for programmers just like you.": "เป็นแอพพลิเคชันจดบันทึก ที่ออกแบบมาเพื่อโปรแกรมเมอร์อย่างคุณ.", + "Website": "เว็บไซต์", + "Development": "การพัฒนา", + " : Development configurations for Boostnote.": " : การตั้งค่าต่างๆสำหรับการพัฒนา Boostnote.", + "Copyright (C) 2017 - 2018 BoostIO": "สงวนลิขสิทธิ์ (C) 2017 - 2018 BoostIO", + "License: GPL v3": "License: GPL v3", + "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.": "คุณสามารถเลือกที่จะเปิดหรือปิดตัวเลือกนี้ได้.", + "Enable analytics to help improve Boostnote": "เปิดการวิเคราะห์ สำหรับการนำไปปรับปรุงพัฒนา Boostnote", + "Crowdfunding": "การระดมทุนสาธารณะ", + "Dear everyone,": "สวัสดีทุกคน,", + "Thank you for using Boostnote!": "ขอขอบคุณที่เลือกใช้ Boostnote!", + "Boostnote is used in about 200 different countries and regions by an awesome community of developers.": "มีการใช้งาน Boostnote จากสังคมผู้ใช้ที่เป็น Developer มากกว่า 200 ประเทศทั่วโลกจากหลากหลายภูมิภาค.", + "To continue supporting this growth, and to satisfy community expectations,": "เพื่อให้เกิดการสนับสนุนให้เกิดการเติบโตอย่างต่อเนื่อง, และเพื่อพัฒนาให้ตรงตามความต้องการของชุมชนผู้ใช้,", + "we would like to invest more time and resources in this project.": "เราต้องใช้เวลา และการลงทุนด้านทรัพยากรสำหรับโครงการนี้.", + "If you like this project and see its potential, you can help by supporting us on OpenCollective!": "ถ้าคุณชอบและมองเห็นความเป็นไปได้ในอนาคต, คุณสามารถช่วยเหลือด้วยการสนับสนุนเราผ่าน OpenCollective!", + "Thanks,": "ขอขอบคุณ,", + "Boostnote maintainers": "กลุ่มผู้พัฒนา Boostnote", + "Support via OpenCollective": "สนับสนุนผ่าน OpenCollective", + "Language": "ภาษา", + "English": "English", + "German": "German", + "French": "French", + "Show \"Saved to Clipboard\" notification when copying": "แสดงการแจ้งเตือน \"บันทึกไปยังคลิปบอร์ด\" เมื่อทำการคัดลอก", + "All Notes": "โน๊ตทั้งหมด", + "Starred": "รายการโปรด", + "Are you sure to ": "คุณแน่ใจหรือไม่ที่จะ ", + " delete": " ลบ", + "this folder?": "โฟลเดอร์นี้?", + "Confirm": "ยืนยัน", + "Cancel": "ยกเลิก", + "Markdown Note": "โน๊ต Markdown", + "This format is for creating text documents. Checklists, code blocks and Latex blocks are available.": "รูปแบบนี้ใช้สำหรับสร้างเอกสารทั่วไป. รองรับการเขียนเช็คลิสต์, แทรกโค้ด และการเขียนโดยใช้ Latex.", + "Snippet Note": "โน๊ต Snippet", + "This format is for creating code snippets. Multiple snippets can be grouped into a single note.": "รูปแบบนี้ใช้สำหรับสร้าง Code snippets. สามารถรวมหลาย Snippets เป็นโน๊ตเดียวกันได้.", + "Tab to switch format": "กด Tab เพื่อเปลี่ยนรูปแบบที่เลือก", + "Updated": "เรียงตามอัพเดท", + "Created": "เรียงตามเวลาที่สร้างโน๊ต", + "Alphabetically": "เรียงตามอักษร", + "Counter": "Counter", + "Default View": "มุมมองปกติ", + "Compressed View": "มุมมองหนาแน่น", + "Search": "ค้นหา", + "Blog Type": "ประเภทของบล็อก", + "Blog Address": "ที่อยู่ของบล็อก", + "Save": "บันทึก", + "Auth": "การยืนยันตัวตน", + "Authentication Method": "รูปแบบการยืนยันตัวตน", + "JWT": "JWT", + "USER": "USER", + "Token": "Token", + "Storage": "แหล่งจัดเก็บ", + "Hotkeys": "คีย์ลัด", + "Show/Hide Boostnote": "แสดง/ซ่อน Boostnote", + "Toggle editor mode": "เปิด/ปิด Editor mode", + "Restore": "กู้คืน", + "Permanent Delete": "ลบถาวร", + "Confirm note deletion": "ยืนยันการลบโน๊ต", + "This will permanently remove this note.": "โน๊ตของคุณจะถูกลบอย่างถาวร.", + "Successfully applied!": "สำเร็จ!", + "Albanian": "Albanian", + "Chinese (zh-CN)": "Chinese (zh-CN)", + "Chinese (zh-TW)": "Chinese (zh-TW)", + "Danish": "Danish", + "Japanese": "Japanese", + "Korean": "Korean", + "Norwegian": "Norwegian", + "Polish": "Polish", + "Portuguese": "Portuguese", + "Spanish": "Spanish", + "You have to save!": "คุณจำเป็นต้องบันทึก!", + "UserName": "UserName", + "Password": "Password", + "Russian": "Russian", + "Hungarian": "Hungarian", + "Thai": "Thai (ภาษาไทย)", + "Command(⌘)": "Command(⌘)", + "Add Storage": "เพิ่มแหล่งจัดเก็บ", + "Name": "ชื่อ", + "Type": "ชนิด", + "File System": "ระบบไฟล์", + "Setting up 3rd-party cloud storage integration:": "ดูวิธีการตั้งค่า หากต้องการใช้งานแบบลิงค์ไฟล์ร่วมกับผู้ให้บริการเก็บข้อมูลบนคลาวด์", + "Cloud-Syncing-and-Backup": "Cloud-Syncing-and-Backup", + "Location": "ที่อยู่", + "Add": "เพิ่ม", + "Unlink Storage": "ยกเลิกการลิงค์ Storage", + "Unlinking removes this linked storage from Boostnote. No data is removed, please manually delete the folder from your hard drive if needed.": "การยกเลิกการลิงค์ จะเป็นการลบการลิงค์แหล่งจัดเก็บออกไปจาก Boostnote. แต่ไฟล์ข้อมูลจะไม่ถูกลบ, หากต้องการลบข้อมูล กรุณาลบโพลเดอร์ของข้อมูลในเครื่องของท่านด้วยตัวเอง.", + "Editor Rulers": "ไม้บรรทัด Editor", + "Enable": "เปิด", + "Disable": "ปิด", + "Sanitization": "Sanitization", + "Only allow secure html tags (recommended)": "อนุญาตเฉพาะ HTML tag ที่มีความปลอดภัย (แนะนำ)", + "Render newlines in Markdown paragraphs as
": "ใช้
แทนอักขระขึ้นบรรทัดใหม่ในข้อความ Markdown", + "Allow styles": "อนุญาตการใช้ styles", + "Allow dangerous html tags": "อนุญาตให้ใช้ html tags ที่ไม่ปลอดภัย", + "Convert textual arrows to beautiful signs. ⚠ This will interfere with using HTML comments in your Markdown.": "แปลงลูกศรจากรูปแบบข้อความให้เป็นสัญลักษณ์. ⚠ สิ่งนี้จะเป็นการแทรกโดยใช้ HTML comment ลงไปใน Markdown ที่คุณเขียน.", + "⚠ 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! ⚠": "⚠ ไม่พบไฟล์แนบในโน๊ตนี้ จากลิงค์ที่คุณได้วาง. คุณสามารถวางลิงค์ที่อ้างอิงไปยังไฟล์แนบ เฉพาะกรณีที่ต้นทาง และปลายทางที่อ้างถึงนั้นอยู่ใน 'แหล่งจัดเก็บ เดียวกัน. กรุณาใช้การลากและวางเพื่อใส่ไฟล์แนบแทน! ⚠", + "Enable smart table editor": "เปิดการใช้ Smart table editor", + "Snippet Default Language": "ทำการ Snippet ภาษาที่เป็นค่าเริ่มต้น" +} diff --git a/package.json b/package.json index 7a32d5f2..a80507c5 100644 --- a/package.json +++ b/package.json @@ -15,7 +15,7 @@ "dev": "node dev-scripts/dev.js" }, "config": { - "electron-version": "2.0.7" + "electron-version": "3.0.3" }, "repository": { "type": "git", @@ -56,7 +56,7 @@ "codemirror": "^5.40.2", "codemirror-mode-elixir": "^1.1.1", "electron-config": "^1.0.0", - "electron-gh-releases": "^2.0.2", + "electron-gh-releases": "^2.0.4", "escape-string-regexp": "^1.0.5", "file-uri-to-path": "^1.0.0", "file-url": "^2.0.2", @@ -126,7 +126,7 @@ "css-loader": "^0.19.0", "devtron": "^1.1.0", "dom-storage": "^2.0.2", - "electron": "2.0.7", + "electron": "3.0.3", "electron-packager": "^12.0.0", "eslint": "^3.13.1", "eslint-config-standard": "^6.2.1", diff --git a/tests/fixtures/markdowns.js b/tests/fixtures/markdowns.js index 69e335e0..312e6c18 100644 --- a/tests/fixtures/markdowns.js +++ b/tests/fixtures/markdowns.js @@ -50,11 +50,14 @@ const smartQuotes = 'This is a "QUOTE".' const breaks = 'This is the first line.\nThis is the second line.' +const shortcuts = 'Ctrl\n\n[[Ctrl]]' + export default { basic, codeblock, katex, checkboxes, smartQuotes, - breaks + breaks, + shortcuts } diff --git a/tests/lib/markdown-test.js b/tests/lib/markdown-test.js index 73b68799..0e330a65 100644 --- a/tests/lib/markdown-test.js +++ b/tests/lib/markdown-test.js @@ -43,3 +43,8 @@ test('Markdown.render() should render line breaks correctly', t => { const renderedNonBreaks = newmd.render(markdownFixtures.breaks) t.snapshot(renderedNonBreaks) }) + +test('Markdown.render() should render shortcuts correctly', t => { + const rendered = md.render(markdownFixtures.shortcuts) + t.snapshot(rendered) +}) diff --git a/tests/lib/snapshots/markdown-test.js.md b/tests/lib/snapshots/markdown-test.js.md index b7251b8d..df5bad53 100644 --- a/tests/lib/snapshots/markdown-test.js.md +++ b/tests/lib/snapshots/markdown-test.js.md @@ -18,6 +18,14 @@ Generated by [AVA](https://ava.li). This is the second line.

␊ ` +## Markdown.render() should render shortcuts correctly + +> Snapshot 1 + + `

Ctrl

␊ +

Ctrl

␊ + ` + ## Markdown.render() should renders KaTeX correctly > Snapshot 1 diff --git a/tests/lib/snapshots/markdown-test.js.snap b/tests/lib/snapshots/markdown-test.js.snap index 9254709e..5b8d1095 100644 Binary files a/tests/lib/snapshots/markdown-test.js.snap and b/tests/lib/snapshots/markdown-test.js.snap differ