/* https://github.com/DiemenDesign/summernote-cleaner */
/* Version: 1.0.9 */
(function(factory) {
if (typeof define === 'function' && define.amd) {
define(['jquery'], factory);
} else if (typeof module === 'object'&&module.exports) {
module.exports = factory(require('jquery'));
} else {
factory(window.jQuery);
}
}
(function ($) {
$.extend(true, $.summernote.lang, {
'en-US': {
cleaner: {
tooltip: 'Cleaner',
not: 'Text has been cleaned!',
limitText: 'Text',
limitHTML: 'HTML'
}
},
'de-DE': {
cleaner: {
tooltip: 'Bereinigen',
not: 'Inhalt wurde bereinigt!',
limitText: 'Text',
limitHTML: 'HTML'
}
},
});
$.extend( $.summernote.options, {
cleaner: {
action: 'both', // both|button|paste 'button' only cleans via toolbar button, 'paste' only clean when pasting content, both does both options.
icon: '',
keepHtml: true,
keepTagContents: ['span'], //Remove tags and keep the contents
badTags: ['applet', 'col', 'colgroup', 'embed', 'noframes', 'noscript', 'script', 'style', 'title', 'meta', 'link', 'head'], //Remove full tags with contents
badAttributes: ['bgcolor', 'border', 'height', 'cellpadding', 'cellspacing', 'lang', 'start', 'style', 'valign', 'width', 'data-(.*?)'], //Remove attributes from remaining tags
limitChars: 0, // 0|# 0 disables option
limitDisplay: 'both', // none|text|html|both
limitStop: false, // true/false
notTimeOut: 850, //time before status message is hidden in miliseconds
keepImages: true,
imagePlaceholder: 'https://via.placeholder.com/200'
}
});
$.extend( $.summernote.plugins, {
'cleaner': function (context) {
var self = this,
ui = $.summernote.ui,
$note = context.layoutInfo.note,
$editor = context.layoutInfo.editor,
options = context.options,
lang = options.langInfo;
if (options.cleaner.action == 'both' || options.cleaner.action == 'button') {
context.memo('button.cleaner', function () {
var button = ui.button({
contents: options.cleaner.icon,
container: options.container,
tooltip: lang.cleaner.tooltip,
placement: options.placement,
click: function () {
if ($note.summernote('createRange').toString())
$note.summernote('pasteHTML', $note.summernote('createRange').toString());
else
$note.summernote('code', cleanPaste($note.summernote('code'), options.cleaner.badTags, options.cleaner.keepTagContents, options.cleaner.badAttributes, options.cleaner.keepImages, options.cleaner.imagePlaceholder, true));
if ($editor.find('.note-status-output').length > 0)
$editor.find('.note-status-output').html(lang.cleaner.not);
}
});
return button.render();
});
}
this.events = {
'summernote.init': function () {
if (options.cleaner.limitChars != 0 || options.cleaner.limitDisplay != 'none'){
var textLength = $editor.find(".note-editable").text().replace(/(<([^>]+)>)/ig, "").replace(/( )/," ");
var codeLength = $editor.find('.note-editable').html();
var lengthStatus = '';
if (textLength.length > options.cleaner.limitChars&&options.cleaner.limitChars > 0)
lengthStatus += 'note-text-danger">';
else
lengthStatus += '">';
if (options.cleaner.limitDisplay == 'text' || options.cleaner.limitDisplay == 'both')
lengthStatus += lang.cleaner.limitText + ': ' + textLength.length;
if (options.cleaner.limitDisplay == 'both')
lengthStatus += ' / ';
if (options.cleaner.limitDisplay == 'html' || options.cleaner.limitDisplay == 'both')
lengthStatus += lang.cleaner.limitHTML + ': ' + codeLength.length;
$editor.find('.note-status-output').html(']+)>)/ig, "").replace(/( )/, " ");
var codeLength = $editor.find('.note-editable').html();
var lengthStatus = '';
if (options.cleaner.limitStop == true && textLength.length >= options.cleaner.limitChars) {
var key = event.keyCode;
allowed_keys = [8, 37, 38, 39, 40, 46];
if ($.inArray(key,allowed_keys) != -1){
$editor.find('.cleanerLimit').removeClass('note-text-danger');
return true;
} else {
$editor.find('.cleanerLimit').addClass('note-text-danger');
event.preventDefault();
event.stopPropagation();
}
} else {
if (textLength.length > options.cleaner.limitChars && options.cleaner.limitChars > 0)
lengthStatus += 'note-text-danger">';
else
lengthStatus += '">';
if (options.cleaner.limitDisplay == 'text' || options.cleaner.limitDisplay == 'both')
lengthStatus += lang.cleaner.limitText + ': '+textLength.length;
if (options.cleaner.limitDisplay == 'both')
lengthStatus += ' / ';
if (options.cleaner.limitDisplay == 'html' || options.cleaner.limitDisplay == 'both')
lengthStatus += lang.cleaner.limitHTML + ': ' + codeLength.length;
$editor.find('.note-status-output').html(' 0 || !!navigator.userAgent.match(/Trident.*rv\:11\./);
var ffox = navigator.userAgent.toLowerCase().indexOf('firefox') > -1;
var text; var isHtmlData = false;
if (msie)
text = window.clipboardData.getData("Text");
else
{
var dataType = 'text/plain';
/*only get the html data if its avaialble else use plain text*/
if (options.cleaner.keepHtml && event.originalEvent.clipboardData.types.indexOf('text/html') > -1) {
dataType = 'text/html';
isHtmlData = true;
}
text = event.originalEvent.clipboardData.getData(dataType);
}
if (text) {
/*clean the text first to prevent issues where code view wasn't updating correctly*/
var cleanedContent = cleanPaste(text, options.cleaner.badTags, options.cleaner.keepTagContents, options.cleaner.badAttributes, options.cleaner.keepImages, options.cleaner.imagePlaceholder, isHtmlData);
if (msie || ffox) {
setTimeout(function () {
$note.summernote('pasteHTML', cleanedContent);
}, 1);
} else {
$note.summernote('pasteHTML', cleanedContent);
}
if ($editor.find('.note-status-output').length > 0) {
$editor.find('.note-status-output').html(lang.cleaner.not);
/*now set a timeout to clear out the message */
setTimeout(function(){
if($editor.find('.note-status-output').html() == lang.cleaner.not){
/*lets fade out the text, then clear it and show the control ready for next time */
$editor.find('.note-status-output').fadeOut(function(){
$(this).html("");
$(this).fadeIn();
});
}
}, options.cleaner.notTimeOut)
}
}
}
}
}
var cleanPaste = function(input, badTags, keepTagContents, badAttributes, keepImages, imagePlaceholder, isHtmlData) {
if(isHtmlData) {
return cleanHtmlPaste(input, badTags, keepTagContents, badAttributes, keepImages, imagePlaceholder);
} else {
return cleanTextPaste(input);
}
};
var cleanTextPaste = function(input) {
var newLines = /(\r\n|\r|\n)/g;
/*lets only replace < and > as these are the culprit for HTML tag recognition */
let inputEscapedHtml = input.replace('<', '<').replace('>', '>');
var parsedInput = inputEscapedHtml.split(newLines);
if(parsedInput.length === 1) { return inputEscapedHtml; }
var output = "";
/*for larger blocks of text (such as multiple paragraphs) match summernote markup */
for (let contentIndex = 0; contentIndex < parsedInput.length; contentIndex++) {
const element = parsedInput[contentIndex];
if(!newLines.test(element)) {
var line = element == '' ? '
' : element;
output += '
' + line + '
' } } return output; } var cleanHtmlPaste = function(input, badTags, keepTagContents, badAttributes, keepImages, imagePlaceholder) { var stringStripper = /( class=(")?Mso[a-zA-Z]+(")?)/gmi; /*remove MS office class crud*/ var output = input.replace(stringStripper, ''); var commentStripper = new RegExp('', 'gmi'); output = output.replace(commentStripper, ''); /*remove MS office comment if else crud */ var commentIfStripper = new RegExp('\v]*>', 'gmi'); output = output.replace(commentIfStripper, ''); var tagStripper = new RegExp('<(/)*(\\?xml:|st1:|o:|v:)[^>\v]*>', 'gmi'); if (!keepImages) { output = output.replace(/ src="(.*?)"/gmi, ' src="' + imagePlaceholder + '"'); } output = output.replace(/ name="(.*?)"/gmi, ' data-title="$1" alt="$1"'); /*remove MS office tag crud*/ output = output.replace(tagStripper, ''); for (var i = 0; i < badTags.length; i++) { const badTag = badTags[i]; /*remove the tag and its contents*/ tagStripper = new RegExp('<' + badTag + '(.|\r|\n)*' + badTag + '[^>\v]*>', 'gmi'); output = output.replace(tagStripper, ''); /*remove tags with no ending tag or rogue ending tags*/ var singletonTagStripper = new RegExp('?' + badTag + '[^>\v]*>', 'gmi'); output = output.replace(singletonTagStripper, ''); } for (var i = 0; i < keepTagContents.length; i++) { /*remove tags only*/ tagStripper = new RegExp('?' + keepTagContents[i] + '[^>\v]*>', 'gmi'); output = output.replace(tagStripper, ' '); } for (var i = 0; i < badAttributes.length; i++) { const badAttribute = badAttributes[i]; /*for attribute matching ensure we match a new line or some kind of space to prevents partial matching for attributes (e.g. color would modify bgcolor tag to be just bg) */ var attributeWithSpeechMarksStripper = new RegExp('(\s|\r\n|\r|\n| )' + badAttribute + '="[^"\v]*"', 'gmi'); output = output.replace(attributeWithSpeechMarksStripper, ''); var attributeWithApostropheStripper = new RegExp('(\s|\r\n|\r|\n| )' + badAttribute + "='[^'\v]*'", 'gmi'); output = output.replace(attributeWithApostropheStripper, ''); } output = output.replace(/ align="(.*?)"/gi, ' class="text-$1"'); output = output.replace(/ class="western"/gi, ''); output = output.replace(/ class=""/gi, ''); output = output.replace(/(.*?)<\/b>/gi, '$1'); output = output.replace(/(.*?)<\/i>/gi, '$1'); output = output.replace(/\s{2,}/g, ' ').trim(); return output; } } }); }));