Skip to content

Instantly share code, notes, and snippets.

@balazs-endresz
Created April 7, 2015 14:34
Show Gist options
  • Save balazs-endresz/5050df80015cdf443f01 to your computer and use it in GitHub Desktop.
Save balazs-endresz/5050df80015cdf443f01 to your computer and use it in GitHub Desktop.
Wagtail rich text editor paragraph issue fix
(function(){
// hallo.js plugin to use paragraphs by default
// https://github.com/bergie/hallo/issues/157
// https://github.com/torchbox/wagtail/issues/559
function getLastChildElement(el){
var lc = el.lastChild;
while(lc && lc.nodeType != 1) {
if(lc.previousSibling)
lc = lc.previousSibling;
else
break;
}
return lc;
}
function canContainText(node){
if(node.nodeType == 1) { //is an element node
var cannotContainText = ['AREA', 'BASE', 'BR', 'COL', 'EMBED', 'HR', 'IMG', 'INPUT', 'KEYGEN', 'LINK', 'MENUITEM', 'META', 'PARAM', 'SOURCE', 'TRACK', 'WBR', 'BASEFONT', 'BGSOUND', 'FRAME', 'ISINDEX'];
var tagName = $(node).prop('tagName').toUpperCase();
return $.inArray(tagName, cannotContainText) == -1;
} else { //is not an element node
return false;
}
}
function setCursor(el){
// http://stackoverflow.com/questions/1125292/how-to-move-cursor-to-end-of-contenteditable-entity
while(getLastChildElement(el) && canContainText(getLastChildElement(el))) {
el = getLastChildElement(el);
}
var range, selection;
if(document.createRange){
//Firefox, Chrome, Opera, Safari, IE 9+
range = document.createRange();//Create a range (a range is a like the selection but invisible)
range.selectNodeContents(el);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
selection = window.getSelection();//get the selection object (allows you to change selection)
selection.removeAllRanges();//remove any selections already made
selection.addRange(range);//make the range you have just created the visible selection
} else if(document.selection){
//IE 8 and lower
range = document.body.createTextRange();//Create a range (a range is a like the selection but invisible)
range.moveToElementText(el);//Select the entire contents of the element with the range
range.collapse(false);//collapse the range to the end point. false means collapse to end rather than the start
range.select();//Select the range (make it the visible selection
}
}
jQuery.widget('IKS.hallorequireparagraphs2', {
_init:function(){
var $el = this.element;
$el.on('hallomodified', function(event, data) {
// console.log("New contents are " + data.content);
// iterate over all the child nodes (including text nodes)
$el.contents().each(function(){
var $this = $(this);
var $p;
// wrap all immediate textNode children in a paragraph
if((this.nodeType == 3) && ($.trim($this.text()).length > 0)){
$p = $this.wrap('</p>');
// we need to move the cursor inside the paragraph to avoid starting a new text node
setCursor($p.get(0));
}
// replace divs with paragraphs if it contains only text
// and also if it contains <br /> too
if($this.is('div')){
if(!$this.children().length || !$.trim($this.text())){
$p = $('<p/>');
$p.append($(this).contents());
$this.replaceWith($p);
// move the cursor to the end of the paragraph
setCursor($p.get(0));
}
}
// Remove the p tag if it has other elements inside it, e.g. ul or ol.
// But do this only if there are no immediate textNodes.
// If we don't do this then additional nesting will be introduced
// when pressing enter after the last list item:
// i.e. we'll end up with divs inside the last top level paragraph.
if($this.is('p') && $this.children().length && $.trim($this.text())){
var has_text_nodes = !!$this.contents().filter(function(){
return (this.nodeType == 3) && ($.trim($(this).text()).length > 0);
}).length;
if(!has_text_nodes){
var $unwrapped = $this.children().unwrap();
// move the cursor to the end of the unwrapped content
setCursor($unwrapped.last().get(0));
}
}
});
});
}
});
}());
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment