Remove all unwanted style attr

This commit is contained in:
Chi Vinh Le
2018-03-25 21:41:45 +02:00
parent 81b98187f1
commit 7d4217d2d7
3 changed files with 153 additions and 49 deletions
@@ -13,6 +13,7 @@ import {
replaceNodeChildren,
selectEndOfNode,
isSelectionInside,
traverse,
} from './lib/dom';
import createAPI from './lib/api';
import Undo from './lib/undo';
@@ -98,6 +99,13 @@ class RTE extends React.Component {
}
handleChange = () => {
// TODO: don't rely on this hack.
// It removes all `style` attr that
// remaining execCommand still add.
traverse(this.ref.htmlEl, n => {
n.removeAttribute && n.removeAttribute('style');
});
this.props.onChange({
text: this.ref.htmlEl.innerText,
html: this.ref.htmlEl.innerHTML,
@@ -116,7 +124,6 @@ class RTE extends React.Component {
this.forEachButton(b => {
b.onSelectionChange && b.onSelectionChange();
});
console.log('sel', getSelectionRange());
};
// Called when Enter was pressed without shift.
@@ -218,6 +225,16 @@ class RTE extends React.Component {
}
};
handleKeyPress = e => {
const character = String.fromCharCode(e.charCode);
if (character) {
insertText(character);
this.handleChange();
e.preventDefault();
return false;
}
};
restoreCheckpoint(html, node, range) {
if (node && range) {
// We need to clone it, otherwise we'll mutate
@@ -317,6 +334,7 @@ class RTE extends React.Component {
id={inputId}
onMouseUp={this.handleMouseUp}
onKeyDown={this.handleKeyDown}
onKeyPress={this.handleKeyPress}
onKeyUp={this.handleKeyUp}
onPaste={this.handlePaste}
onCut={this.handleCut}
@@ -6,7 +6,7 @@ import {
getSelectedNodesExpanded,
outdentNode,
selectEndOfNode,
endWithNewLine,
indentNodes,
} from '../lib/dom';
function execCommand() {
@@ -14,19 +14,13 @@ function execCommand() {
if (bq) {
outdentNode(bq, true);
} else {
const node = document.createElement('blockquote');
// Expanded selection means we always select whole lines.
const selectedNodes = getSelectedNodesExpanded();
if (selectedNodes.length) {
const firstNode = selectedNodes[0];
firstNode.parentNode.insertBefore(node, firstNode);
selectedNodes.forEach(n => {
node.appendChild(n);
});
endWithNewLine(node);
const node = indentNodes(selectedNodes, 'blockquote');
selectEndOfNode(node);
} else {
const node = document.createElement('blockquote');
node.appendChild(document.createElement('br'));
insertNodes(node);
selectEndOfNode(node);
@@ -1,3 +1,41 @@
/**
* Traverse DOM tree until callback returns anything.
*/
export function traverse(node, callback) {
let result;
for (let i = 0; i < node.childNodes.length; i++) {
const child = node.childNodes[i];
result = callback(child);
if (result === undefined) {
result = traverse(child, callback);
}
if (result !== undefined) {
return result;
}
}
}
/**
* Traverse DOM tree backwards until callback returns anything.
* If hits limitTo returns null.
*/
export function traverseUp(node, callback, limitTo) {
let result;
if (node.isSameNode(limitTo)) {
return null;
}
while (node.parentNode) {
node = node.parentNode;
result = callback(node);
if (result !== undefined) {
return result;
}
if (limitTo && node.isSameNode(limitTo)) {
return null;
}
}
}
/**
* Find ancestor with given tag or whith callback returning true.
* If `limitTo` is passed, the search is limited to this container.
@@ -7,19 +45,17 @@ export function findAncestor(node, tagOrCallback, limitTo) {
typeof tagOrCallback === 'function'
? tagOrCallback
: n => n.tagName === tagOrCallback;
if (node.isSameNode(limitTo)) {
return null;
}
while (node.parentNode) {
node = node.parentNode;
if (callback(node)) {
return node;
}
if (limitTo && node.isSameNode(limitTo)) {
return null;
}
}
return null;
return (
traverseUp(
node,
n => {
if (callback(n)) {
return n;
}
},
limitTo
) || null
);
}
/**
@@ -30,17 +66,13 @@ export function findChild(node, tagOrCallback) {
typeof tagOrCallback === 'function'
? tagOrCallback
: n => n.tagName === tagOrCallback;
for (let i = 0; i < node.childNodes.length; i++) {
const child = node.childNodes[i];
if (callback(child)) {
return child;
}
const found = findChild(child, tagOrCallback);
if (found) {
return found;
}
}
return null;
return (
traverse(node, n => {
if (callback(n)) {
return n;
}
}) || null
);
}
/**
@@ -220,19 +252,13 @@ export function getSelectionRange() {
return selection.rangeCount ? selection.getRangeAt(0) : null;
}
// Adds a 'br' marker at the end of the node.
export function endWithNewLine(node) {
// Adds a bogus 'br' at the end of the node if not existant.
export function addBogusBR(node) {
if (!isBlockElement(node)) {
return;
}
if (
!node.lastChild ||
node.lastChild.tagName !== 'BR' ||
node.lastChild.className !== 'coral-rte-end-marker'
) {
const br = document.createElement('br');
br.className = 'coral-rte-end-marker';
node.appendChild(br);
if (!node.lastChild || !isBogusBR(node.lastChild)) {
node.appendChild(document.createElement('br'));
}
}
@@ -275,6 +301,17 @@ export function insertNewLine(changeSelection) {
const el = document.createElement('br');
insertNodes(el);
// If we are adding to the end of the node, we also need
// to add a bogus br.
if (!el.nextSibling) {
el.parentNode.appendChild(document.createElement('br'));
}
// Adding directly before a block element needs also a bogus br.
if (el.nextSibling && isBlockElement(el.nextSibling)) {
el.parentNode.insertBefore(document.createElement('br'), el.nextSibling);
}
// Calculate next selection.
const range = document.createRange();
if (el.nextSibling) {
@@ -282,10 +319,6 @@ export function insertNewLine(changeSelection) {
range.setStart(el.parentNode, offset);
range.setEnd(el.parentNode, offset);
} else {
// We need to add a <br> marker at the end, because we can't
// select the last <br>.
endWithNewLine(el.parentNode);
const offset = el.parentNode.childNodes.length - 1;
range.setStart(el.parentNode, offset);
range.setEnd(el.parentNode, offset);
@@ -342,6 +375,14 @@ export function getLeftOfNode(node) {
return result;
}
export function isBogusBR(node) {
return (
(!node.previousSibling || !isBlockElement(node.previousSibling)) &&
node.tagName === 'BR' &&
(!node.nextSibling || isBlockElement(node.previousSibling))
);
}
/**
* Returns an array of all nodes after `node`.
*/
@@ -356,7 +397,11 @@ export function getRightOfNode(node) {
cur = cur.nextSibling;
result.push(cur);
}
if (cur.nextSibling && cur.nextSibling.tagName === 'BR') {
if (
cur.nextSibling &&
cur.nextSibling.tagName === 'BR' &&
!isBogusBR(cur.nextSibling)
) {
result.push(cur.nextSibling);
}
return result;
@@ -457,11 +502,28 @@ export function getSelectedChildren(ancestor) {
* Removes node and assimilate its children with the parent.
*/
export function outdentNode(node, changeSelection) {
/// Remove bogus br
if (node.lastChild && node.lastChild.tagName === 'BR') {
node.removeChild(node.lastChild);
}
// A new line to substitute the missing block element.
const needExtraLine =
node.nextSibling &&
!isBlockElement(node.nextSibling) &&
!isBlockElement(node.lastChild);
const parentNode = node.parentNode;
const offset = indexOfChildNode(parentNode, node);
while (node.firstChild) {
parentNode.insertBefore(node.firstChild, node);
}
if (needExtraLine) {
parentNode.insertBefore(document.createElement('BR'), node);
}
parentNode.removeChild(node);
if (changeSelection) {
@@ -472,6 +534,36 @@ export function outdentNode(node, changeSelection) {
}
}
/**
* Indent children.
*/
export function indentNodes(nodes, tagName) {
const node = document.createElement(tagName);
// Remove bogus BR if the blockquote is the last element.
// Otherwise there will be an unwanted empty line.
const lastNode = nodes[nodes.length - 1];
if (
lastNode.nextSibling === lastNode.parentNode.lastChild &&
isBogusBR(lastNode.parentNode.lastChild)
) {
lastNode.parentNode.removeChild(lastNode.parentNode.lastChild);
}
const firstNode = nodes[0];
// Remove previous br as it is not needed
if (firstNode.previousSibling && firstNode.previousSibling.tagName === 'BR') {
firstNode.parentNode.removeChild(firstNode.previousSibling);
}
// Finally indent.
firstNode.parentNode.insertBefore(node, firstNode);
nodes.forEach(n => {
node.appendChild(n);
});
return node;
}
function cloneNodeAndRangeHelper(node, range, rangeCloned) {
const nodeCloned = node.cloneNode(false);
for (let i = 0; i < node.childNodes.length; i++) {