Skip to content

Commit a998cba

Browse files
committed
Merge branch 't/10071' into major
2 parents fde96a9 + b1e3afd commit a998cba

File tree

2 files changed

+94
-20
lines changed

2 files changed

+94
-20
lines changed

core/filter.js

Lines changed: 75 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,19 @@
4949
*/
5050
this.editor = null;
5151

52+
/**
53+
* Enter mode used by filter when deciding how to strip disallowed elements.
54+
*
55+
* For editor's filter it will be set to {@link CKEDITOR.config#enterMode} unless this
56+
* is a blockless (see {@link CKEDITOR.editor#blockless}) editor - in this case
57+
* {@link CKEDITOR#ENTER_BR} will be forced.
58+
*
59+
* For standalone filter it will be by default set to {@link CKEDITOR#ENTER_P}.
60+
*
61+
* @property {CKEDITOR.ENTER_P/CKEDITOR.ENTER_DIV/CKEDITOR.ENTER_BR}
62+
*/
63+
this.enterMode = CKEDITOR.ENTER_P;
64+
5265
this._ = {
5366
// Optimized allowed content rules.
5467
rules: {},
@@ -61,7 +74,8 @@
6174
var editor = this.editor = editorOrRules;
6275
this.customConfig = true;
6376

64-
var allowedContent = editor.config.allowedContent;
77+
var allowedContent = editor.config.allowedContent,
78+
enterMode;
6579

6680
// Disable filter completely by setting config.allowedContent = true.
6781
if ( allowedContent === true ) {
@@ -72,8 +86,10 @@
7286
if ( !allowedContent )
7387
this.customConfig = false;
7488

75-
// Add editor's default rules.
76-
this.allow( 'p br', 'default', 1 );
89+
// Force ENTER_BR for blockless editable.
90+
this.enterMode = enterMode = ( editor.blockless ? CKEDITOR.ENTER_BR : editor.config.enterMode );
91+
92+
this.allow( 'br ' + ( enterMode == CKEDITOR.ENTER_P ? 'p' : enterMode == CKEDITOR.ENTER_DIV ? 'div' : '' ), 'default', 1 );
7793
this.allow( allowedContent, 'config', 1 );
7894
this.allow( editor.config.extraAllowedContent, 'extra', 1 );
7995

@@ -190,19 +206,20 @@
190206
}, CKEDITOR.NODE_ELEMENT, true );
191207

192208
var element,
193-
toBeChecked = [];
209+
toBeChecked = [],
210+
enterTag = [ 'p', 'br', 'div' ][ this.enterMode - 1 ];
194211

195212
// Remove elements in reverse order - from leaves to root, to avoid conflicts.
196213
while ( ( element = toBeRemoved.pop() ) )
197-
removeElement( element, toBeChecked );
214+
removeElement( element, enterTag, toBeChecked );
198215

199216
// Check elements that have been marked as invalid (e.g. li as child of body after ul has been removed).
200217
while ( ( element = toBeChecked.pop() ) ) {
201218
if ( element.parent &&
202219
element.parent.type != CKEDITOR.NODE_DOCUMENT_FRAGMENT &&
203220
!DTD[ element.parent.name ][ element.name ]
204221
)
205-
removeElement( element, toBeChecked );
222+
removeElement( element, enterTag, toBeChecked );
206223
}
207224
},
208225

@@ -1086,38 +1103,55 @@
10861103
return true;
10871104
}
10881105

1106+
function createBr() {
1107+
return new CKEDITOR.htmlParser.element( 'br' );
1108+
}
1109+
10891110
// Whether this is an inline element or text.
10901111
function inlineNode( node ) {
10911112
return node.type == CKEDITOR.NODE_TEXT ||
10921113
node.type == CKEDITOR.NODE_ELEMENT && DTD.$inline[ node.name ];
10931114
}
10941115

1116+
function isBrOrBlock( node ) {
1117+
return node.type == CKEDITOR.NODE_ELEMENT &&
1118+
( node.name == 'br' || DTD.$block[ node.name ] );
1119+
}
1120+
10951121
// Try to remove element in the best possible way.
10961122
//
10971123
// @param {Array} toBeChecked After executing this function
10981124
// this array will contain elements that should be checked
10991125
// because they were marked as potentially in wrong context (e.g. li in body).
1100-
function removeElement( element, toBeChecked ) {
1126+
function removeElement( element, enterTag, toBeChecked ) {
11011127
var name = element.name;
11021128

1103-
if ( DTD.$empty[ name ] || !element.children.length )
1104-
element.remove();
1105-
else if ( DTD.$block[ name ] || name == 'tr' )
1106-
stripElement( element, toBeChecked );
1107-
else
1129+
if ( DTD.$empty[ name ] || !element.children.length ) {
1130+
// Special case - hr in br mode should be replaced with br, not removed.
1131+
if ( name == 'hr' && enterTag == 'br' )
1132+
element.replaceWith( createBr() );
1133+
else
1134+
element.remove();
1135+
} else if ( DTD.$block[ name ] || name == 'tr' ) {
1136+
if ( enterTag == 'br' )
1137+
stripBlockBr( element, toBeChecked );
1138+
else
1139+
stripBlock( element, enterTag, toBeChecked );
1140+
} else
11081141
element.replaceWithChildren();
11091142
}
11101143

1111-
// Strip element, but leave its content.
1112-
function stripElement( element, toBeChecked ) {
1144+
// Strip element block, but leave its content.
1145+
// Works in 'div' and 'p' enter modes.
1146+
function stripBlock( element, enterTag, toBeChecked ) {
11131147
var children = element.children;
11141148

1115-
// First, check if element's children may be wrapped with <p>.
1116-
// Ignore that <p> may not be allowed in element.parent.
1149+
// First, check if element's children may be wrapped with <p/div>.
1150+
// Ignore that <p/div> may not be allowed in element.parent.
11171151
// This will be fixed when removing parent, because in all known cases
1118-
// parent will was also marked to be removed.
1119-
if ( checkChildren( children, 'p' ) ) {
1120-
element.name = 'p';
1152+
// parent will be also marked to be removed.
1153+
if ( checkChildren( children, enterTag ) ) {
1154+
element.name = enterTag;
11211155
element.attributes = {};
11221156
return;
11231157
}
@@ -1134,7 +1168,7 @@
11341168
// insert this child into newly created paragraph.
11351169
if ( shouldAutoP && inlineNode( child ) ) {
11361170
if ( !p ) {
1137-
p = new CKEDITOR.htmlParser.element( 'p' );
1171+
p = new CKEDITOR.htmlParser.element( enterTag );
11381172
p.insertAfter( element );
11391173
}
11401174
p.add( child, 0 );
@@ -1157,6 +1191,27 @@
11571191
element.remove();
11581192
}
11591193

1194+
// Prepend/append block with <br> if isn't
1195+
// already prepended/appended with <br> or block and
1196+
// isn't first/last child of its parent.
1197+
// Then replace element with its children.
1198+
// <p>a</p><p>b</p> => <p>a</p><br>b => a<br>b
1199+
function stripBlockBr( element, toBeChecked ) {
1200+
var br;
1201+
1202+
if ( element.previous && !isBrOrBlock( element.previous ) ) {
1203+
br = createBr();
1204+
br.insertBefore( element );
1205+
}
1206+
1207+
if ( element.next && !isBrOrBlock( element.next ) ) {
1208+
br = createBr();
1209+
br.insertAfter( element );
1210+
}
1211+
1212+
element.replaceWithChildren();
1213+
}
1214+
11601215
//
11611216
// TRANSFORMATIONS --------------------------------------------------------
11621217
//

core/htmlparser/node.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,25 @@
6767
node.next = this;
6868
next && ( next.previous = this );
6969

70+
this.parent = node.parent;
71+
},
72+
73+
/**
74+
* Insert this node before given one.
75+
*
76+
* @param {CKEDITOR.htmlParser.node} node The node that will follow this element.
77+
*/
78+
insertBefore: function( node ) {
79+
var children = node.parent.children,
80+
index = CKEDITOR.tools.indexOf( children, node );
81+
82+
children.splice( index, 0, this );
83+
84+
this.next = node;
85+
this.previous = node.previous;
86+
node.previous && ( node.previous.next = this );
87+
node.previous = this;
88+
7089
this.parent = node.parent;
7190
}
7291
};

0 commit comments

Comments
 (0)