0
Fork 0
mirror of https://github.com/fastmail/Squire.git synced 2025-01-03 05:00:13 -05:00

Better cleanTree fn.

- Better checks to precisely trim white space only at beginning and end of block
- Don't keep the contents of <head> or <style> tags if inserted into body.
- Always keep styles to preserve fidelity on paste.

Fixes #95.
This commit is contained in:
Neil Jenkins 2015-06-19 10:56:52 +07:00
parent 926f40d033
commit 7fc3ab55c3
4 changed files with 84 additions and 88 deletions

View file

@ -1720,10 +1720,20 @@ var stylesRewriters = {
and whitespace nodes. and whitespace nodes.
2. Convert inline tags into our preferred format. 2. Convert inline tags into our preferred format.
*/ */
var cleanTree = function ( node, allowStyles ) { var cleanTree = function cleanTree ( node ) {
var children = node.childNodes, var children = node.childNodes,
i, l, child, nodeName, nodeType, rewriter, childLength, blockParent, i, l, child, nodeName, nodeType, rewriter, childLength,
data, j, ll; startsWithWS, endsWithWS, data;
blockParent = node;
while ( isInline( blockParent ) ) {
blockParent = blockParent.parentNode;
}
if ( !isBlock( blockParent ) ) {
blockParent = null;
}
contentWalker.root = blockParent;
for ( i = 0, l = children.length; i < l; i += 1 ) { for ( i = 0, l = children.length; i < l; i += 1 ) {
child = children[i]; child = children[i];
nodeName = child.nodeName; nodeName = child.nodeName;
@ -1736,55 +1746,43 @@ var cleanTree = function ( node, allowStyles ) {
} else if ( !allowedBlock.test( nodeName ) && } else if ( !allowedBlock.test( nodeName ) &&
!isInline( child ) ) { !isInline( child ) ) {
i -= 1; i -= 1;
l += childLength - 1; if ( nodeName === 'HEAD' || nodeName === 'STYLE' ) {
node.replaceChild( empty( child ), child ); node.removeChild( child );
l -= 1;
} else {
l += childLength - 1;
node.replaceChild( empty( child ), child );
}
continue; continue;
} else if ( !allowStyles && child.style.cssText ) {
child.removeAttribute( 'style' );
} }
if ( childLength ) { if ( childLength ) {
cleanTree( child, allowStyles ); cleanTree( child );
} }
} else { } else {
if ( nodeType === TEXT_NODE ) { if ( nodeType === TEXT_NODE ) {
data = child.data; data = child.data;
// Use \S instead of notWS, because we want to remove nodes // Use \s instead of notWS, because we want to remove nodes
// which are just nbsp, in order to cleanup <div>nbsp<br></div> // which are just nbsp, in order to cleanup <div>nbsp<br></div>
// construct. // construct.
if ( /\S/.test( data ) ) { startsWithWS = /\s/.test( data.charAt( 0 ) );
// If the parent node is inline, don't trim this node as endsWithWS = /\s/.test( data.charAt( data.length - 1 ) );
// it probably isn't at the end of the block. if ( !startsWithWS && !endsWithWS ) {
if ( isInline( node ) ) {
continue;
}
j = 0;
ll = data.length;
if ( !i || !isInline( children[ i - 1 ] ) ) {
while ( j < ll && !notWS.test( data.charAt( j ) ) ) {
j += 1;
}
if ( j ) {
child.data = data = data.slice( j );
ll -= j;
}
}
if ( i + 1 === l || !isInline( children[ i + 1 ] ) ) {
j = ll;
while ( j > 0 && !notWS.test( data.charAt( j - 1 ) ) ) {
j -= 1;
}
if ( j < ll ) {
child.data = data.slice( 0, j );
}
}
continue; continue;
} }
// If we have just white space, it may still be important if it if ( startsWithWS ) {
// separates two inline nodes, e.g. "<a>link</a> <a>link</a>". contentWalker.currentNode = child;
else if ( i && i + 1 < l && if ( !blockParent || !contentWalker.previousNode() ) {
isInline( children[ i - 1 ] ) && data = data.replace( /^\s+/g, '' );
isInline( children[ i + 1 ] ) ) { }
child.data = ' '; }
if ( endsWithWS ) {
contentWalker.currentNode = child;
if ( !blockParent || !contentWalker.nextNode() ) {
data = data.replace( /\s+$/g, '' );
}
}
if ( data ) {
child.data = data;
continue; continue;
} }
} }
@ -3319,7 +3317,7 @@ proto.setHTML = function ( html ) {
div.innerHTML = html; div.innerHTML = html;
frag.appendChild( empty( div ) ); frag.appendChild( empty( div ) );
cleanTree( frag, true ); cleanTree( frag );
cleanupBRs( frag ); cleanupBRs( frag );
fixContainer( frag ); fixContainer( frag );
@ -3473,7 +3471,7 @@ proto.insertHTML = function ( html, isPaste ) {
}; };
addLinks( frag ); addLinks( frag );
cleanTree( frag, true ); cleanTree( frag );
cleanupBRs( frag ); cleanupBRs( frag );
removeEmptyInlines( frag ); removeEmptyInlines( frag );
frag.normalize(); frag.normalize();

File diff suppressed because one or more lines are too long

View file

@ -172,10 +172,20 @@ var stylesRewriters = {
and whitespace nodes. and whitespace nodes.
2. Convert inline tags into our preferred format. 2. Convert inline tags into our preferred format.
*/ */
var cleanTree = function ( node, allowStyles ) { var cleanTree = function cleanTree ( node ) {
var children = node.childNodes, var children = node.childNodes,
i, l, child, nodeName, nodeType, rewriter, childLength, blockParent, i, l, child, nodeName, nodeType, rewriter, childLength,
data, j, ll; startsWithWS, endsWithWS, data;
blockParent = node;
while ( isInline( blockParent ) ) {
blockParent = blockParent.parentNode;
}
if ( !isBlock( blockParent ) ) {
blockParent = null;
}
contentWalker.root = blockParent;
for ( i = 0, l = children.length; i < l; i += 1 ) { for ( i = 0, l = children.length; i < l; i += 1 ) {
child = children[i]; child = children[i];
nodeName = child.nodeName; nodeName = child.nodeName;
@ -188,55 +198,43 @@ var cleanTree = function ( node, allowStyles ) {
} else if ( !allowedBlock.test( nodeName ) && } else if ( !allowedBlock.test( nodeName ) &&
!isInline( child ) ) { !isInline( child ) ) {
i -= 1; i -= 1;
l += childLength - 1; if ( nodeName === 'HEAD' || nodeName === 'STYLE' ) {
node.replaceChild( empty( child ), child ); node.removeChild( child );
l -= 1;
} else {
l += childLength - 1;
node.replaceChild( empty( child ), child );
}
continue; continue;
} else if ( !allowStyles && child.style.cssText ) {
child.removeAttribute( 'style' );
} }
if ( childLength ) { if ( childLength ) {
cleanTree( child, allowStyles ); cleanTree( child );
} }
} else { } else {
if ( nodeType === TEXT_NODE ) { if ( nodeType === TEXT_NODE ) {
data = child.data; data = child.data;
// Use \S instead of notWS, because we want to remove nodes // Use \s instead of notWS, because we want to remove nodes
// which are just nbsp, in order to cleanup <div>nbsp<br></div> // which are just nbsp, in order to cleanup <div>nbsp<br></div>
// construct. // construct.
if ( /\S/.test( data ) ) { startsWithWS = /\s/.test( data.charAt( 0 ) );
// If the parent node is inline, don't trim this node as endsWithWS = /\s/.test( data.charAt( data.length - 1 ) );
// it probably isn't at the end of the block. if ( !startsWithWS && !endsWithWS ) {
if ( isInline( node ) ) {
continue;
}
j = 0;
ll = data.length;
if ( !i || !isInline( children[ i - 1 ] ) ) {
while ( j < ll && !notWS.test( data.charAt( j ) ) ) {
j += 1;
}
if ( j ) {
child.data = data = data.slice( j );
ll -= j;
}
}
if ( i + 1 === l || !isInline( children[ i + 1 ] ) ) {
j = ll;
while ( j > 0 && !notWS.test( data.charAt( j - 1 ) ) ) {
j -= 1;
}
if ( j < ll ) {
child.data = data.slice( 0, j );
}
}
continue; continue;
} }
// If we have just white space, it may still be important if it if ( startsWithWS ) {
// separates two inline nodes, e.g. "<a>link</a> <a>link</a>". contentWalker.currentNode = child;
else if ( i && i + 1 < l && if ( !blockParent || !contentWalker.previousNode() ) {
isInline( children[ i - 1 ] ) && data = data.replace( /^\s+/g, '' );
isInline( children[ i + 1 ] ) ) { }
child.data = ' '; }
if ( endsWithWS ) {
contentWalker.currentNode = child;
if ( !blockParent || !contentWalker.nextNode() ) {
data = data.replace( /\s+$/g, '' );
}
}
if ( data ) {
child.data = data;
continue; continue;
} }
} }

View file

@ -1294,7 +1294,7 @@ proto.setHTML = function ( html ) {
div.innerHTML = html; div.innerHTML = html;
frag.appendChild( empty( div ) ); frag.appendChild( empty( div ) );
cleanTree( frag, true ); cleanTree( frag );
cleanupBRs( frag ); cleanupBRs( frag );
fixContainer( frag ); fixContainer( frag );
@ -1448,7 +1448,7 @@ proto.insertHTML = function ( html, isPaste ) {
}; };
addLinks( frag ); addLinks( frag );
cleanTree( frag, true ); cleanTree( frag );
cleanupBRs( frag ); cleanupBRs( frag );
removeEmptyInlines( frag ); removeEmptyInlines( frag );
frag.normalize(); frag.normalize();