Skip to content

Commit

Permalink
Fix blank row block insertions in chrome and safari
Browse files Browse the repository at this point in the history
  • Loading branch information
Oliver Pulges committed Dec 18, 2015
1 parent b6b95e1 commit 80a3a75
Show file tree
Hide file tree
Showing 4 changed files with 87 additions and 38 deletions.
40 changes: 37 additions & 3 deletions src/commands/formatBlock.js
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@
nbIdx;

for (var i = elements.length; i--;) {
if (elements[i].innerHTML.replace(/[\uFEFF]/g, '') === "") {
if (elements[i].innerHTML.replace(/[\uFEFF]/g, '') === "" && (newBlockElements.length === 0 || elements[i] !== newBlockElements[newBlockElements.length - 1])) {
// If cleanup removes some new block elements. remove them from newblocks array too
nbIdx = wysihtml5.lang.array(newBlockElements).indexOf(elements[i]);
if (nbIdx > -1) {
Expand Down Expand Up @@ -397,8 +397,13 @@
}
composer.selection.splitElementAtCaret(outerInlines.parent, fragment);
} else {
// Otherwise just insert
var fc = fragment.firstChild,
lc = fragment.lastChild;

range.insertNode(fragment);
// restore range position as it might get lost in webkit sometimes
range.setStartBefore(fc);
range.setEndAfter(lc);
}
}
}
Expand Down Expand Up @@ -527,7 +532,21 @@
startNode = getRangeNode(r.startContainer, r.startOffset),
endNode = getRangeNode(r.endContainer, r.endOffset),
prevNode = (r.startContainer === startNode && startNode.nodeType === 3 && !isWhitespaceBefore(startNode, r.startOffset)) ? startNode : wysihtml5.dom.domNode(startNode).prev({nodeTypes: [1,3], ignoreBlankTexts: true}),
nextNode = ((r.endContainer.nodeType === 1 && r.endContainer.childNodes[r.endOffset] === endNode) || (r.endContainer === endNode && endNode.nodeType === 3 && !isWhitespaceAfter(endNode, r.endOffset))) ? endNode : wysihtml5.dom.domNode(getRangeNode(r.endContainer, r.endOffset)).next({nodeTypes: [1,3], ignoreBlankTexts: true}),
nextNode = (
(
r.endContainer.nodeType === 1 &&
r.endContainer.childNodes[r.endOffset] === endNode &&
(
endNode.nodeType === 1 ||
!isWhitespaceAfter(endNode, r.endOffset) &&
!wysihtml5.dom.domNode(endNode).is.rangyBookmark()
)
) || (
r.endContainer === endNode &&
endNode.nodeType === 3 &&
!isWhitespaceAfter(endNode, r.endOffset)
)
) ? endNode : wysihtml5.dom.domNode(endNode).next({nodeTypes: [1,3], ignoreBlankTexts: true}),
content = r.extractContents(),
fragment = composer.doc.createDocumentFragment(),
similarOuterBlock = similarOptions ? wysihtml5.dom.getParentElement(rangeStartContainer, similarOptions, null, composer.element) : null,
Expand All @@ -536,6 +555,11 @@
wrapper, blocks, children,
firstc, lastC;

if (wysihtml5.dom.domNode(nextNode).is.rangyBookmark()) {
endNode = nextNode;
nextNode = endNode.nextSibling;
}

trimBlankTextsAndBreaks(content);

if (options && options.nodeName === "BLOCKQUOTE") {
Expand Down Expand Up @@ -585,6 +609,16 @@
}
injectFragmentToRange(fragment, r, composer, firstOuterBlock);
removeSurroundingLineBreaks(prevNode, nextNode, composer);

// Fix webkit madness by inserting linebreak rangy after cursor marker to blank last block
// (if it contains rangy bookmark, so selection can be restored later correctly)
if (blocks.length > 0 &&
(
typeof blocks[blocks.length - 1].lastChild === "undefined" || wysihtml5.dom.domNode(blocks[blocks.length - 1].lastChild).is.rangyBookmark()
)
) {
blocks[blocks.length - 1].appendChild(composer.doc.createElement('br'));
}
return blocks;
}

Expand Down
14 changes: 7 additions & 7 deletions src/dom/dom_node.js
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,6 @@
return nodes;
}

// Returns if node is the rangy selection bookmark element (that must not be taken into account in most situatons and is removed on selection restoring)
function isBookmark(n) {
return n && n.nodeType === 1 && n.classList.contains('rangySelectionBoundary');
}

wysihtml5.dom.domNode = function(node) {
var defaultNodeTypes = [wysihtml5.ELEMENT_NODE, wysihtml5.TEXT_NODE];

Expand All @@ -30,6 +25,11 @@
return node && node.nodeType === wysihtml5.TEXT_NODE && (regx).test(node.data);
},

// Returns if node is the rangy selection bookmark element (that must not be taken into account in most situatons and is removed on selection restoring)
rangyBookmark: function() {
return node && node.nodeType === 1 && node.classList.contains('rangySelectionBoundary');
},

visible: function() {
var isVisible = !(/^\s*$/g).test(wysihtml5.dom.getTextContent(node));

Expand Down Expand Up @@ -66,7 +66,7 @@
}

if (
isBookmark(prevNode) || // is Rangy temporary boomark element (bypass)
wysihtml5.dom.domNode(prevNode).is.rangyBookmark() || // is Rangy temporary boomark element (bypass)
(!wysihtml5.lang.array(types).contains(prevNode.nodeType)) || // nodeTypes check.
(options && options.ignoreBlankTexts && wysihtml5.dom.domNode(prevNode).is.emptyTextNode(true)) // Blank text nodes bypassed if set
) {
Expand All @@ -86,7 +86,7 @@
}

if (
isBookmark(nextNode) || // is Rangy temporary boomark element (bypass)
wysihtml5.dom.domNode(nextNode).is.rangyBookmark() || // is Rangy temporary boomark element (bypass)
(!wysihtml5.lang.array(types).contains(nextNode.nodeType)) || // nodeTypes check.
(options && options.ignoreBlankTexts && wysihtml5.dom.domNode(nextNode).is.emptyTextNode(true)) // blank text nodes bypassed if set
) {
Expand Down
59 changes: 34 additions & 25 deletions src/selection/selection.js
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@
}
};

blankNode.appendChild(document.createTextNode(wysihtml5.INVISIBLE_SPACE));
blankNode.appendChild(container.ownerDocument.createTextNode(wysihtml5.INVISIBLE_SPACE));
blankNode.className = '_wysihtml5-temp-caret-fix';
blankNode.style.display = 'block';
blankNode.style.minWidth = '1px';
Expand Down Expand Up @@ -948,8 +948,8 @@
if (wysihtml5.browser.supportsSelectionModify()) {
this._selectLine_W3C();
} else if (r.nativeRange && r.nativeRange.getBoundingClientRect) {
// For IE Edge as it ditched the old api and did not fully implement the new one (as expected)*/
this._selectLineUniversal();
// For IE Edge as it ditched the old api and did not fully implement the new one (as expected)
this._selectLineUniversal();
}
},

Expand All @@ -965,7 +965,6 @@
} else {
return node.data && node.data.length || 0;
}
// body...
},
anode = s.anchorNode.nodeType === 1 ? s.anchorNode.childNodes[s.anchorOffset] : s.anchorNode,
fnode = s.focusNode.nodeType === 1 ? s.focusNode.childNodes[s.focusOffset] : s.focusNode;
Expand Down Expand Up @@ -1074,7 +1073,16 @@
r.moveEnd('character', 1);
} else if (r.startContainer.nodeType === 1 && r.startContainer.childNodes[r.startOffset] && r.startContainer.childNodes[r.startOffset].nodeType === 3 && r.startContainer.childNodes[r.startOffset].data.length > 0) {
r.moveEnd('character', 1);
} else if (r.startOffset > 0 && ( r.startContainer.nodeType === 3 || (r.startContainer.nodeType === 1 && !isLineBreakingElement(prevNode(r.startContainer.childNodes[r.startOffset - 1]))))) {
} else if (
r.startOffset > 0 &&
(
r.startContainer.nodeType === 3 ||
(
r.startContainer.nodeType === 1 &&
!isLineBreakingElement(prevNode(r.startContainer.childNodes[r.startOffset - 1]))
)
)
) {
r.moveStart('character', -1);
}
}
Expand All @@ -1084,6 +1092,7 @@

// Is probably just empty line as can not be expanded
rect = r.nativeRange.getBoundingClientRect();
// If startnode is not line break allready move the start position of range by -1 character until clientRect top changes;
do {
amount = r.moveStart('character', -1);
testRect = r.nativeRange.getBoundingClientRect();
Expand All @@ -1094,31 +1103,31 @@
}
count++;
} while (amount !== 0 && !found && count < 2000);

count = 0;
found = false;
rect = r.nativeRange.getBoundingClientRect();
do {
amount = r.moveEnd('character', 1);
testRect = r.nativeRange.getBoundingClientRect();
if (!testRect || Math.floor(testRect.bottom) !== Math.floor(rect.bottom)) {
r.moveEnd('character', -1);

// Fix a IE line end marked by linebreak element although caret is before it
// If causes problems should be changed to be applied only to IE
if (r.endContainer && r.endContainer.nodeType === 1 && r.endContainer.childNodes[r.endOffset] && r.endContainer.childNodes[r.endOffset].nodeType === 1 && r.endContainer.childNodes[r.endOffset].nodeName === "BR" && r.endContainer.childNodes[r.endOffset].previousSibling) {
if (r.endContainer.childNodes[r.endOffset].previousSibling.nodeType === 1) {
r.setEnd(r.endContainer.childNodes[r.endOffset].previousSibling, r.endContainer.childNodes[r.endOffset].previousSibling.childNodes.length);
} else if (r.endContainer.childNodes[r.endOffset].previousSibling.nodeType === 3) {
r.setEnd(r.endContainer.childNodes[r.endOffset].previousSibling, r.endContainer.childNodes[r.endOffset].previousSibling.data.length);

if (r.endContainer !== this.contain || (this.contain.lastChild && this.contain.childNodes[r.endOffset] !== this.contain.lastChild)) {
do {
amount = r.moveEnd('character', 1);
testRect = r.nativeRange.getBoundingClientRect();
if (!testRect || Math.floor(testRect.bottom) !== Math.floor(rect.bottom)) {
r.moveEnd('character', -1);

// Fix a IE line end marked by linebreak element although caret is before it
// If causes problems should be changed to be applied only to IE
if (r.endContainer && r.endContainer.nodeType === 1 && r.endContainer.childNodes[r.endOffset] && r.endContainer.childNodes[r.endOffset].nodeType === 1 && r.endContainer.childNodes[r.endOffset].nodeName === "BR" && r.endContainer.childNodes[r.endOffset].previousSibling) {
if (r.endContainer.childNodes[r.endOffset].previousSibling.nodeType === 1) {
r.setEnd(r.endContainer.childNodes[r.endOffset].previousSibling, r.endContainer.childNodes[r.endOffset].previousSibling.childNodes.length);
} else if (r.endContainer.childNodes[r.endOffset].previousSibling.nodeType === 3) {
r.setEnd(r.endContainer.childNodes[r.endOffset].previousSibling, r.endContainer.childNodes[r.endOffset].previousSibling.data.length);
}
}
found = true;
}

found = true;
}
count++;
} while (amount !== 0 && !found && count < 2000);

count++;
} while (amount !== 0 && !found && count < 2000);
}
r.select();
this.includeRangyRangeHelpers();
},
Expand Down
12 changes: 9 additions & 3 deletions test/commands/formatBlock_test.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ if (wysihtml5.browser.supported()) {

// formatblock (alignment, headings, paragraph, pre, blockquote)
asyncTest("Format block", function() {
expect(16);
expect(18);
var that = this,
parserRules = {
tags: {
Expand Down Expand Up @@ -153,12 +153,18 @@ if (wysihtml5.browser.supported()) {
editor.composer.commands.remove('formatBlock');
equal(editableElement.innerHTML.toLowerCase(), 'test<br>foo<br>test', "Adding and removing block format restored initial situation");

editor.setValue("test<br>foo</br>test", true);
editor.setValue("test<br>foo<br>test", true);
that.setCaretTo(editor, editor.editableElement.childNodes[2], 1);
editor.composer.commands.exec('formatBlock', "h1");
editor.composer.commands.remove('formatBlock');
equal(editableElement.innerHTML.toLowerCase(), 'test<br>foo<br>test', "Adding and removing block format restored initial situation (with caret)");


editor.setValue("test<br>foo<br><br>", true);
that.setCaretTo(editor, editor.editableElement, 4);
editor.composer.commands.exec('formatBlock', "h1");
equal(editableElement.innerHTML.toLowerCase(), 'test<br>foo<h1><br></h1>', "Adding block before last enter worked");
editor.composer.commands.exec('insertHTML', "test");
equal(editableElement.innerHTML.toLowerCase(), 'test<br>foo<h1>test<br></h1>', "... and caret is in the new empty block");

start();
});
Expand Down

0 comments on commit 80a3a75

Please sign in to comment.