A Pen by Michael Stabile on CodePen.
Created
April 6, 2020 09:19
-
-
Save nazarbek7/e0e08acd134859b05bbe9f44c335afde to your computer and use it in GitHub Desktop.
Simple WYSIWYG (contenteditable) + paste from MS Word
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css" rel="stylesheet" integrity="sha384-wvfXpqpZZVQGK6TAh5PVlGOfQNHSoD2xbE+QkPxCAFlNEevoEH3Sl0sibVcOQVnN" crossorigin="anonymous"> | |
<body> | |
<div class="container"> | |
<h2>Simple WYSIWYG</h2> | |
<div class="editor-toolbar"> | |
<div class="dropdown"> | |
<button class="btn btn-default dropdown-toggle" type="button" id="headingMenu" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> | |
Heading | |
<span class="caret"></span> | |
</button> | |
<ul class="dropdown-menu" aria-labelledby="headingMenu"> | |
<li><a href="javascript:void(0)" onclick="runCommand(this, 'formatBlock', 'h1')" class="h1">Heading 1</a></li> | |
<li><a href="javascript:void(0)" onclick="runCommand(this, 'formatBlock', 'h2')" class="h2">Heading 2</a></li> | |
<li><a href="javascript:void(0)" onclick="runCommand(this, 'formatBlock', 'h3')" class="h3">Heading 3</a></li> | |
<li><a href="javascript:void(0)" onclick="runCommand(this, 'formatBlock', 'h4')" class="h4">Heading 4</a></li> | |
</ul> | |
</div> | |
<a href="javascript:void(0)" role="button" class="toolbar-btn unselectable" onclick="runCommand(this, 'bold', null)" unselectable="on"><i class="fa fa-bold"></i></a> | |
<a href="javascript:void(0)" role="button" class="toolbar-btn" onclick="runCommand(this, 'italic', null)"><i class="fa fa-italic"></i></a> | |
<a href="javascript:void(0)" role="button" onclick="runCommand(this, 'underline', null)"><i class="fa fa-underline"></i></a> | |
<a href="javascript:void(0)" role="button" onclick="runCommand(this, 'indent', null)"><i class="fa fa-indent"></i></a> | |
<a href="javascript:void(0)" role="button" onclick="runCommand(this, 'insertUnorderedList', null)"><i class="fa fa-list-ul"></i></a> | |
<a href="javascript:void(0)" role="button" onclick="runCommand(this, 'insertOrderedList', null)"><i class="fa fa-list-ol"></i></a> | |
<a href="javascript:void(0)" role="button" onclick="runCommand(this, 'createLink', null)"><i class="fa fa-link"></i></a> | |
<a href="javascript:void(0)" role="button" onclick="runCommand(this, 'unlink', null)"><i class="fa fa-unlink"></i></a> | |
<a href="javascript:void(0)" role="button" onclick="runCommand(this, 'redo', null)"><i class="fa fa-repeat"></i></a> | |
<a href="javascript:void(0)" role="button" onclick="runCommand(this, 'undo', null)"><i class="fa fa-undo"></i></a> | |
</div> | |
<div id="editor" contenteditable="true"></div> | |
<textarea name="messageText" id="messageText" class="hidden"></textarea> | |
<div class="row"> | |
<div class="col-sm-2"> | |
<button id="submit" class="btn btn-default btn-block" type="submit">Submit</button> | |
</div> | |
</div> | |
</div> | |
</body> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Move caret back to | |
function placeCaretAtEnd(el) { | |
el.focus(); | |
if (typeof window.getSelection != "undefined" && typeof document.createRange != "undefined") { | |
var range = document.createRange(); | |
range.selectNodeContents(el); | |
range.collapse(false); | |
var sel = window.getSelection(); | |
sel.removeAllRanges(); | |
sel.addRange(range); | |
} else if (typeof document.body.createTextRange != "undefined") { | |
var textRange = document.body.createTextRange(); | |
textRange.moveToElementText(el); | |
textRange.collapse(false); | |
textRange.select(); | |
} | |
} | |
// Clean HTML tags using sanitize-html | |
function cleanHtml() { | |
let value = $("#editor").html(); | |
let clean = sanitizeHtml(value, { | |
allowedTags: ['div', 'blockquote', 'b', 'strong', 'i', 'em', 'ul', 'ol', 'li'], | |
allowedAttributes: { | |
'blockquote': ['style'] | |
} | |
}); | |
let cleanValue = clean.trim(); | |
setContent(); | |
} | |
// Paste from MS Word *CREDIT: https://gist.github.com/sbrin/6801034 | |
(function($) { | |
$.fn.msword_html_filter = function(options) { | |
let settings = $.extend( {}, options); | |
function word_filter(editor){ | |
let content = editor.html(); | |
// Word comments like conditional comments etc | |
content = content.replace(/<!--[\s\S]+?-->/gi, ''); | |
// Remove comments, scripts (e.g., msoShowComment), XML tag, VML content, | |
// MS Office namespaced tags, and a few other tags | |
content = content.replace(/<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi, ''); | |
// Convert <s> into <strike> for line-though | |
content = content.replace(/<(\/?)s>/gi, "<$1strike>"); | |
// Replace nbsp entites to char since it's easier to handle | |
//content = content.replace(/ /gi, "\u00a0"); | |
content = content.replace(/ /gi, ' '); | |
// Convert <span style="mso-spacerun:yes">___</span> to string of alternating | |
// breaking/non-breaking spaces of same length | |
content = content.replace(/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi, function(str, spaces) { | |
return (spaces.length > 0) ? spaces.replace(/./, " ").slice(Math.floor(spaces.length/2)).split("").join("\u00a0") : ''; | |
}); | |
editor.html(content); | |
// Parse out list indent level for lists | |
$('p', editor).each(function(){ | |
let str = $(this).attr('style'); | |
let matches = /mso-list:\w+ \w+([0-9]+)/.exec(str); | |
if (matches) { | |
$(this).data('_listLevel', parseInt(matches[1], 10)); | |
} | |
}); | |
// Parse Lists | |
let last_level=0; | |
let pnt = null; | |
$('p', editor).each(function(){ | |
let cur_level = $(this).data('_listLevel'); | |
if(cur_level != undefined){ | |
let txt = $(this).text(); | |
let list_tag = '<ul></ul>'; | |
if (/^\s*\w+\./.test(txt)) { | |
let matches = /([0-9])\./.exec(txt); | |
if (matches) { | |
let start = parseInt(matches[1], 10); | |
list_tag = start>1 ? '<ol start="' + start + '"></ol>' : '<ol></ol>'; | |
}else{ | |
list_tag = '<ol></ol>'; | |
} | |
} | |
if(cur_level>last_level){ | |
if(last_level==0){ | |
$(this).before(list_tag); | |
pnt = $(this).prev(); | |
}else{ | |
pnt = $(list_tag).appendTo(pnt); | |
} | |
} | |
if(cur_level<last_level){ | |
for(let i=0; i<last_level-cur_level; i++){ | |
pnt = pnt.parent(); | |
} | |
} | |
$('span:first', this).remove(); | |
pnt.append('<li>' + $(this).html().replace(/\d+\./g, '') + '</li>') | |
$('b:empty').remove(); | |
$(this).remove(); | |
last_level = cur_level; | |
}else{ | |
last_level = 0; | |
} | |
}) | |
$('[style]', editor).removeAttr('style'); | |
$('[align]', editor).removeAttr('align'); | |
$('span', editor).replaceWith(function() {return $(this).contents();}); | |
$('span:empty', editor).remove(); | |
$("[class^='Mso']", editor).removeAttr('class'); | |
$('p:empty', editor).remove(); | |
} | |
return this.each(function() { | |
let self = this; | |
$(self).on('keyup paste', function(){ | |
setTimeout(function() { | |
let content = $(self).html(); | |
(/class="?Mso|style="[^"]*\bmso-|style='[^'']*\bmso-|w:WordDocument/i.test( content )) ? word_filter( $(self) ) : cleanHtml(); | |
}, 400); | |
}); | |
}); | |
}; | |
})( jQuery ) | |
$(function(){ | |
$('#editor').msword_html_filter(); | |
}) | |
function setContent() { | |
let value = $(this).html(); | |
let el = $("#editor").get(0); | |
placeCaretAtEnd(el); | |
} | |
//### EVENTS/ACTIONS ###// | |
//execCommand(aCommandName, aShowDefaultUI, aValueArgument) | |
function runCommand(el, commandName, arg) { | |
if (commandName === "createLink") { | |
let argument = prompt("Insert link:"); | |
document.execCommand(commandName, false, argument); | |
}else { | |
document.execCommand(commandName, false, arg); | |
} | |
$("#editor").focus(); | |
return false; | |
} | |
// Capture wysiwyg val and assign to textarea val | |
// $("#editor").keyup(function() { | |
// let value = $(this).html(); | |
// $("#messageText").val(value); | |
// }); | |
// Show submitted data | |
$('#submit').click(function(e) { | |
e.preventDefault(); | |
let content = $("#editor").html().trim(); | |
alert("VALUE SUBMITTED: \n" + content); | |
}); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script> | |
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/js/bootstrap.min.js"></script> | |
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/index.min.js"></script> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
@grey: #e4e4e4; | |
@white: #fff; | |
@border: #cecece; | |
body { | |
font-family:"Open Sans", sans-serif; | |
background: @grey; | |
margin: 20px 0; | |
.h1 { | |
font-size: 2em; | |
} | |
.h2 { | |
font-size: 1.5em; | |
} | |
.h3 { | |
font-size: 1.17em; | |
} | |
.h4 { | |
font-size: 1em; | |
} | |
} | |
.editor-toolbar { | |
background: @white; | |
border: 1px solid @border; | |
margin: 0; | |
padding: 0; | |
a { | |
display: inline-block; | |
padding: 8px 12px; | |
color: #000; | |
&:hover { | |
background: @grey; | |
} | |
} | |
.active { | |
background: @grey; | |
} | |
.dropdown { | |
display: inline-block; | |
.btn-default { | |
border: none; | |
&:focus, &:hover { | |
background: none; | |
} | |
} | |
a { | |
margin: 0; | |
} | |
} | |
} | |
#editor { | |
resize: vertical; | |
overflow: auto; | |
line-height: 1.5; | |
background: @white; | |
border: 1px solid @border; | |
border-top: none; | |
min-height: 150px; | |
box-shadow: none; | |
padding: 8px 16px; | |
margin: 0 0 10px 0; | |
font-size: 14px; | |
&:focus{ | |
outline: none; | |
} | |
} | |
// IE fix for toolbar buttons | |
unselectable { | |
-webkit-user-select: none; | |
-moz-user-select: -moz-none; | |
-ms-user-select: none; | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/3.3.7/css/bootstrap.min.css" rel="stylesheet" /> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment