diff --git a/CHANGELOG.md b/CHANGELOG.md index dd6c508..d8ed5ad 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,10 +1,26 @@ # EasyMDE Changelog -All notable changes to this project will be documented in this file. +All notable changes to easymde will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [Unreleased] + +Merge 2.7.0 with https://github.com/Ionaru/easy-markdown-editor/pull/71 @jeroenvo + +## [2.7.0] - 2019-07-13 +### Added +- `previewClass` option for overwriting the preview screen class ([#99]). + +### Fixed +- Updated dependencies to resolve potential security issue. +- Resolved small code style issues shown by new eslint rules. + +## [2.6.1] - 2019-06-17 +### Fixed +- Error when toggling between ordered and unordered lists (Thanks to [@roryok], [#93]). +- Keyboard shortcuts for custom actions not working (Thanks to [@ysykzheng], [#75]). + +## [2.6.0] - 2019-04-15 ### Added - Contributing guide (Thanks to [@roipoussiere], [#54]). - Issue templates. @@ -13,7 +29,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Finish rewrite of README (Thanks to [@roipoussiere], [#54]). - Image and link prompt fill with "https://" by default. -- Link to markdown guide to https://www.markdownguide.org/basic-syntax/. +- Link to markdown guide to . ### Fixed - Backwards compatibility in the API with SimpleMDE 1.0.0 ([#41]). diff --git a/gulpfile.js b/gulpfile.js index fdc76f5..99fba6f 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -2,7 +2,7 @@ var gulp = require('gulp'); var cleanCSS = require('gulp-clean-css'); -var uglify = require('gulp-uglify'); +var terser = require('gulp-terser'); var concat = require('gulp-concat'); var header = require('gulp-header'); var buffer = require('vinyl-buffer'); @@ -31,7 +31,7 @@ function scripts() { return browserify({entries: './src/js/easymde.js', standalone: 'EasyMDE'}).bundle() .pipe(source('easymde.min.js')) .pipe(buffer()) - .pipe(uglify()) + .pipe(terser()) .pipe(header(banner, {pkg: pkg})) .pipe(gulp.dest('./dist/')); } diff --git a/src/js/easymde.js b/src/js/easymde.js index 3ff39a0..cb4f50b 100644 --- a/src/js/easymde.js +++ b/src/js/easymde.js @@ -1,4 +1,3 @@ -/*global require,module*/ 'use strict'; var CodeMirror = require('codemirror'); require('codemirror/addon/edit/continuelist.js'); @@ -122,6 +121,11 @@ function createToolbarButton(options, enableTooltips, shortcuts) { el.setAttribute('type', 'button'); enableTooltips = (enableTooltips == undefined) ? true : enableTooltips; + // Properly hande custom shortcuts + if( options.name && options.name in shortcuts ){ + bindings[options.name] = options.action; + } + if (options.title && enableTooltips) { el.title = createTooltip(options.title, options.action, shortcuts); @@ -169,7 +173,7 @@ function createToolbarButton(options, enableTooltips, shortcuts) { function createSep() { var el = document.createElement('i'); el.className = 'separator'; - el.textContent = '|'; + el.innerHTML = '|'; return el; } @@ -840,9 +844,23 @@ function togglePreview(editor) { var toolbar_div = wrapper.previousSibling; var toolbar = editor.options.toolbar ? editor.toolbarElements.preview : false; var preview = wrapper.lastChild; - if (!preview || !/editor-preview/.test(preview.className)) { + if (!preview || !/editor-preview-full/.test(preview.className)) { + preview = document.createElement('div'); - preview.className = 'editor-preview'; + preview.className = 'editor-preview-full'; + + if (editor.options.previewClass) { + + if (Array.isArray(editor.options.previewClass)) { + for (var i = 0; i < editor.options.previewClass.length; i++) { + preview.className += (' ' + editor.options.previewClass[i]); + } + + } else if (typeof editor.options.previewClass === 'string') { + preview.className += (' ' + editor.options.previewClass); + } + } + wrapper.appendChild(preview); } if (/editor-preview-active/.test(preview.className)) { @@ -885,7 +903,6 @@ function _replaceSelection(cm, active, startEnd, url) { Object.assign(startPoint, cm.getCursor('start')); Object.assign(endPoint, cm.getCursor('end')); if (url) { - start = start.replace('#url#', url); end = end.replace('#url#', url); } if (active) { @@ -1010,13 +1027,27 @@ function _toggleLine(cm, name) { var map = { 'quote': '>', 'unordered-list': '*', - 'ordered-list': 'd+.', + 'ordered-list': '\\d+.', }; var rt = new RegExp(map[name]); return char && rt.test(char); }; + var _toggle = function (name, text, untoggleOnly) { + var arr = listRegexp.exec(text); + var char = _getChar(name, line); + if (arr !== null) { + if (_checkChar(name, arr[2])) { + char = ''; + } + text = arr[1] + char + arr[3] + text.replace(whitespacesRegexp, '').replace(repl[name], '$1'); + } else if (untoggleOnly == false){ + text = char + ' ' + text; + } + return text; + }; + var line = 1; for (var i = startPoint.line; i <= endPoint.line; i++) { (function (i) { @@ -1024,16 +1055,13 @@ function _toggleLine(cm, name) { if (stat[name]) { text = text.replace(repl[name], '$1'); } else { - var arr = listRegexp.exec(text); - var char = _getChar(name, line); - if (arr !== null) { - if (_checkChar(name, arr[2])) { - char = ''; - } - text = arr[1] + char + arr[3] + text.replace(whitespacesRegexp, '').replace(repl[name], '$1'); - } else { - text = char + ' ' + text; + // If we're toggling unordered-list formatting, check if the current line + // is part of an ordered-list, and if so, untoggle that first. + // Workaround for https://github.com/Ionaru/easy-markdown-editor/issues/92 + if (name == 'unordered-list') { + text = _toggle('ordered-list', text, true); } + text = _toggle(name, text, false); line += 1; } cm.replaceRange(text, { @@ -1160,7 +1188,7 @@ function humanFileSize(bytes, units) { // Merge the properties of one object into another. function _mergeProperties(target, source) { for (var property in source) { - if (source.hasOwnProperty(property)) { + if (Object.prototype.hasOwnProperty.call(source, property)) { if (source[property] instanceof Array) { target[property] = source[property].concat(target[property] instanceof Array ? target[property] : []); } else if ( @@ -1491,7 +1519,7 @@ function EasyMDE(options) { // Loop over the built in buttons, to get the preferred order for (var key in toolbarBuiltInButtons) { - if (toolbarBuiltInButtons.hasOwnProperty(key)) { + if (Object.prototype.hasOwnProperty.call(toolbarBuiltInButtons, key)) { if (key.indexOf('separator-') != -1) { options.toolbar.push('|'); } @@ -1503,9 +1531,13 @@ function EasyMDE(options) { } } + // Editor preview styling class. + if (!Object.prototype.hasOwnProperty.call(options, 'previewClass')) { + options.previewClass = 'editor-preview'; + } // Handle status bar - if (!options.hasOwnProperty('status')) { + if (!Object.prototype.hasOwnProperty.call(options, 'status')) { if (options.uploadImage) { options.status = ['upload-image', 'autosave', 'lines', 'words', 'cursor']; } else { @@ -1710,7 +1742,12 @@ EasyMDE.prototype.render = function (el) { if (options.shortcuts[key] !== null && bindings[key] !== null) { (function (key) { keyMaps[fixShortcut(options.shortcuts[key])] = function () { - bindings[key](self); + var action = bindings[key]; + if (typeof action === 'function') { + action(self); + } else if (typeof action === 'string') { + window.open(action, '_blank'); + } }; })(key); } @@ -1874,7 +1911,7 @@ EasyMDE.prototype.autosave = function () { } m = m < 10 ? '0' + m : m; - el.textContent = 'Autosaved: ' + h + ':' + m + ' ' + dd; + el.innerHTML = 'Autosaved: ' + h + ':' + m + ' ' + dd; } this.autosaveTimeoutId = setTimeout(function () { @@ -1993,6 +2030,19 @@ EasyMDE.prototype.createSideBySide = function () { if (!preview || !/editor-preview-side/.test(preview.className)) { preview = document.createElement('div'); preview.className = 'editor-preview-side'; + + if (this.options.previewClass) { + + if (Array.isArray(this.options.previewClass)) { + for (var i = 0; i < this.options.previewClass.length; i++) { + preview.className += (' ' + this.options.previewClass[i]); + } + + } else if (typeof this.options.previewClass === 'string') { + preview.className += (' ' + this.options.previewClass); + } + } + wrapper.parentNode.insertBefore(preview, wrapper.nextSibling); } @@ -2176,25 +2226,25 @@ EasyMDE.prototype.createStatusbar = function (status) { if (name === 'words') { defaultValue = function (el) { - el.textContent = wordCount(cm.getValue()); + el.innerHTML = wordCount(cm.getValue()); }; onUpdate = function (el) { - el.textContent = wordCount(cm.getValue()); + el.innerHTML = wordCount(cm.getValue()); }; } else if (name === 'lines') { defaultValue = function (el) { - el.textContent = cm.lineCount(); + el.innerHTML = cm.lineCount(); }; onUpdate = function (el) { - el.textContent = cm.lineCount(); + el.innerHTML = cm.lineCount(); }; } else if (name === 'cursor') { defaultValue = function (el) { - el.textContent = '0:0'; + el.innerHTML = '0:0'; }; onUpdate = function (el) { var pos = cm.getCursor(); - el.textContent = pos.line + ':' + pos.ch; + el.innerHTML = pos.line + ':' + pos.ch; }; } else if (name === 'autosave') { defaultValue = function (el) { @@ -2204,7 +2254,7 @@ EasyMDE.prototype.createStatusbar = function (status) { }; } else if (name === 'upload-image') { defaultValue = function (el) { - el.textContent = options.imageTexts.sbInit; + el.innerHTML = options.imageTexts.sbInit; }; } diff --git a/types/easymde-test.ts b/types/easymde-test.ts index 2d5f5b7..f240ee9 100644 --- a/types/easymde-test.ts +++ b/types/easymde-test.ts @@ -7,6 +7,7 @@ const editor = new EasyMDE({ drawTable: 'Cmd-Alt-T', toggleFullScreen: null }, + previewClass: 'my-custom-class', spellChecker: false, onToggleFullScreen: (full: boolean) => { console.log('FullscreenToggled', full); @@ -28,3 +29,33 @@ editor.codemirror.setOption('readOnly', true); EasyMDE.toggleItalic = (editor: EasyMDE) => { console.log('SomeButtonOverride'); }; + +const editor2 = new EasyMDE({ + autoDownloadFontAwesome: undefined, + previewClass: ['my-custom-class', 'some-other-class'], + toolbar: [{ + name: 'bold', + action: EasyMDE.toggleBold, + className: 'fa fa-bolt', + title: 'Bold', + }, '|', { // Separator + name: 'alert', + action: (editor) => { + alert('This is from a custom button action!'); + // Custom functions have access to the `editor` instance. + }, + className: 'fa fa-star', + title: 'A Custom Button', + noDisable: undefined, + noMobile: false, + }, '|', { + name: 'link', + action: 'https://github.com/Ionaru/easy-markdown-editor', + className: 'fa fab fa-github', + title: 'A Custom Link', + noDisable: true, + noMobile: true, + }] +}); + +editor2.clearAutosavedValue(); diff --git a/types/easymde.d.ts b/types/easymde.d.ts index 07e9d4c..2055b77 100644 --- a/types/easymde.d.ts +++ b/types/easymde.d.ts @@ -1,16 +1,16 @@ // This file is based on https://github.com/DefinitelyTyped/DefinitelyTyped/blob/master/types/simplemde/index.d.ts, // which is written by Scalesoft and licensed under the MIT license: -// +// // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to deal // in the Software without restriction, including without limitation the rights // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell // copies of the Software, and to permit persons to whom the Software is // furnished to do so, subject to the following conditions: -// +// // The above copyright notice and this permission notice shall be included in all // copies or substantial portions of the Software. -// +// // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE @@ -100,6 +100,7 @@ declare namespace EasyMDE { lineWrapping?: boolean; parsingConfig?: ParsingOptions; placeholder?: string; + previewClass?: string | ReadonlyArray; previewRender?: (markdownPlaintext: string, previewElement: HTMLElement) => string; promptURLs?: boolean; renderingConfig?: RenderingOptions;