/** * A markdown output plugin for CKEDITOR. * Uses showdown.js for conversion and DOMPurifier for HTML filtering. * * This software is licensed under MIT License. * * Copyright (c) 2019 Jan Böhmer * * 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 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ (function() { function overrideDataProcessor(editor) { //Both showdown and DOMPurify must be loaded if(typeof(showdown) == 'undefined') return; if (typeof(DOMPurify) == 'undefined') return; //If the dataprocessor were already be overriden do nothing if(editor.dataProcessor.markdown) return; var converter = new showdown.Converter(); //Set some useful options on Showdown converter.setFlavor('github'); converter.setOption('tables', true); converter.setOption('strikethrough', true); converter.setOption('parseImgDimensions', true); converter.setOption('smartIndentationFix', true); editor.dataProcessor = { toDataFormat: function(html, fixForBody) { //html = html.replace(/(\r\n|\n|\r)/gm,""); html = html.replace('
', '\n'); //Support for strikethrough in markdown html = html.replace('', '').replace('', ''); return converter.makeMarkdown(html); }, toHtml: function(data){ //Convert markdown to HTML and remove unsafe things from it. var unsafe = converter.makeHtml(data); return DOMPurify.sanitize(unsafe); }, //Mark this dataprocessor markdown: true }; //Set the original data if(editor.markdown_unchangedData) { editor.setData(editor.markdown_unchangedData); editor.markdown_unchangedData = null; } } CKEDITOR.plugins.add( 'markdown', { //requires: 'entities', onLoad: function() { CKEDITOR.addCss( //Show borders on tables generated by Showdown 'table {\n' + ' border-width: 1px 0 0 1px;\n' + ' border-color: #bbb;\n' + ' border-style: solid;\n' + ' }\n' + '\n' + 'table td, table th {\n' + ' border-width: 0 1px 1px 0;\n' + ' border-color: #bbb;\n' + ' border-style: solid;\n' + ' padding: 10px;\n' + '}' + //Show code blocks 'pre {\n' + ' display: block;\n' + ' padding: 9.5px;\n' + ' margin: 0 0 10px;\n' + ' font-size: 13px;\n' + ' line-height: 1.42857143;\n' + ' color: #333;\n' + ' word-break: break-all;\n' + ' word-wrap: break-word;\n' + ' background-color: #f5f5f5;\n' + ' border: 1px solid #ccc;\n' + ' border-radius: 4px;\n' + '}' + 'padding: 0;\n' + ' font-size: inherit;\n' + ' color: inherit;\n' + ' white-space: pre-wrap;\n' + ' background-color: transparent;\n' + ' border-radius: 0;' + 'code, kbd, pre, samp {\n' + ' font-family: Menlo, Monaco, Consolas, "Courier New", monospace;\n' + '}' + //Show images small 'img {\n' + ' max-width: 35%;\n' + ' vertical-align: middle;' + '}' ); }, beforeInit: function( editor ) { var config = editor.config; CKEDITOR.tools.extend( config, { basicEntities: false, entities: false, fillEmptyBlocks: false }, true ); editor.filter.disable(); }, init: function( editor ) { var config = editor.config; var rootPath = this.path; //We override the dataprocessor later (after we loaded the needes scripts), sow we need the save untouched data now. editor.markdown_unchangedData = editor.getData(); if (typeof(showdown) == 'undefined') { CKEDITOR.scriptLoader.load(rootPath + 'js/showdown.min.js', function() { overrideDataProcessor(editor); }, CKEDITOR, true); } if (typeof(DOMPurify) == 'undefined') { CKEDITOR.scriptLoader.load(rootPath + 'js/purify.min.js', function() { overrideDataProcessor() }, CKEDITOR, true); } }, afterInit: function( editor ) { //Override the data processor with our for Markdown. overrideDataProcessor(editor); } } ); } )();