@@ -3486,15 +3486,16 @@ var config = {
3486
3486
italicTag : '<em>' ,
3487
3487
3488
3488
// Rules that are applied when filtering pasted content
3489
- pastedHtmlFilter : {
3489
+ pastedHtmlRules : {
3490
3490
3491
3491
// Elements and their attributes to keep in pasted text
3492
3492
allowedElements : {
3493
3493
'a' : {
3494
3494
'href' : true
3495
3495
} ,
3496
3496
'strong' : { } ,
3497
- 'em' : { }
3497
+ 'em' : { } ,
3498
+ 'br' : { }
3498
3499
} ,
3499
3500
3500
3501
// Elements that have required attributes.
@@ -3509,8 +3510,15 @@ var config = {
3509
3510
transformElements : {
3510
3511
'b' : 'strong' ,
3511
3512
'i' : 'em'
3512
- }
3513
+ } ,
3514
+
3515
+ // A list of elements which should be split into paragraphs.
3516
+ splitIntoBlocks : [ 'h1' , 'h2' , 'h3' , 'h4' , 'h5' , 'h6' , 'p' , 'blockquote' ] ,
3517
+
3518
+ // A list of HTML block level elements.
3519
+ blockLevelElements : [ 'h1' , 'h2' , 'h3' , 'h4' , 'h5' , 'h6' , 'div' , 'p' , 'pre' , 'hr' , 'blockquote' , 'article' , 'figure' , 'header' , 'footer' , 'ul' , 'ol' , 'li' , 'section' , 'table' , 'video' ]
3513
3520
}
3521
+
3514
3522
} ;
3515
3523
3516
3524
@@ -3657,8 +3665,7 @@ Editable.prototype.enable = function($elem, normalize) {
3657
3665
3658
3666
if ( normalize ) {
3659
3667
$elem . each ( function ( index , el ) {
3660
- content . normalizeTags ( el ) ;
3661
- content . normalizeSpaces ( el ) ;
3668
+ content . tidyHtml ( el ) ;
3662
3669
} ) ;
3663
3670
}
3664
3671
@@ -3908,13 +3915,26 @@ var block = (function() {
3908
3915
3909
3916
var clipboard = ( function ( ) {
3910
3917
var allowedElements , requiredAttributes , transformElements ;
3918
+ var blockLevelElements , splitIntoBlocks ;
3911
3919
var whitespaceOnly = / ^ \s * $ / ;
3920
+ var blockPlaceholder = '<!-- BLOCK -->' ;
3912
3921
3913
3922
var updateConfig = function ( config ) {
3914
- var filter = config . pastedHtmlFilter ;
3915
- allowedElements = filter . allowedElements || { } ;
3916
- requiredAttributes = filter . requiredAttributes || { } ;
3917
- transformElements = filter . transformElements || { } ;
3923
+ var i , name , rules = config . pastedHtmlRules ;
3924
+ allowedElements = rules . allowedElements || { } ;
3925
+ requiredAttributes = rules . requiredAttributes || { } ;
3926
+ transformElements = rules . transformElements || { } ;
3927
+
3928
+ blockLevelElements = { } ;
3929
+ for ( i = 0 ; i < rules . blockLevelElements . length ; i ++ ) {
3930
+ name = rules . blockLevelElements [ i ] ;
3931
+ blockLevelElements [ name ] = true ;
3932
+ }
3933
+ splitIntoBlocks = { } ;
3934
+ for ( i = 0 ; i < rules . splitIntoBlocks . length ; i ++ ) {
3935
+ name = rules . splitIntoBlocks [ i ] ;
3936
+ splitIntoBlocks [ name ] = true ;
3937
+ }
3918
3938
} ;
3919
3939
3920
3940
updateConfig ( config ) ;
@@ -3957,7 +3977,6 @@ var clipboard = (function() {
3957
3977
getContenteditableContainer : function ( document ) {
3958
3978
var pasteHolder = $ ( '<div>' )
3959
3979
. attr ( 'contenteditable' , true )
3960
- . addClass ( 'filteredPaste_pasteIntoArea_xxx' )
3961
3980
. css ( {
3962
3981
position : 'fixed' ,
3963
3982
left : '5px' ,
@@ -3980,7 +3999,19 @@ var clipboard = (function() {
3980
3999
// Filter pasted content
3981
4000
var pastedString = this . filterHtmlElements ( element ) ;
3982
4001
4002
+ // Handle Blocks
4003
+ var blocks = pastedString . split ( blockPlaceholder ) ;
4004
+ blocks = blocks . filter ( function ( entry ) {
4005
+ return ! whitespaceOnly . test ( entry ) ;
4006
+ } ) ;
4007
+ pastedString = blocks . join ( '<br><br>' ) ;
4008
+
4009
+ // Clean Whitesapce
4010
+ // todo: make configurable
4011
+ pastedString = this . cleanWhitespace ( pastedString ) ;
4012
+
3983
4013
// Trim pasted Text
4014
+ // todo: make configurable
3984
4015
if ( pastedString ) {
3985
4016
pastedString = string . trim ( pastedString ) ;
3986
4017
}
@@ -4010,11 +4041,25 @@ var clipboard = (function() {
4010
4041
var nodeName = child . nodeName . toLowerCase ( ) ;
4011
4042
nodeName = this . transformNodeName ( nodeName ) ;
4012
4043
4013
- if ( this . shouldKeepNode ( nodeName , child ) && ! whitespaceOnly . test ( content ) ) {
4044
+ if ( this . shouldKeepNode ( nodeName , child ) ) {
4014
4045
var attributes = this . filterAttributes ( nodeName , child ) ;
4015
- return '<' + nodeName + attributes + '>' + content + '</' + nodeName + '>' ;
4046
+ if ( nodeName === 'br' ) {
4047
+ return '<' + nodeName + attributes + '>' ;
4048
+ } else if ( ! whitespaceOnly . test ( content ) ) {
4049
+ return '<' + nodeName + attributes + '>' + content + '</' + nodeName + '>' ;
4050
+ } else {
4051
+ return content ;
4052
+ }
4016
4053
} else {
4017
- return content ;
4054
+ if ( splitIntoBlocks [ nodeName ] ) {
4055
+ return blockPlaceholder + content + blockPlaceholder ;
4056
+ } else if ( blockLevelElements [ nodeName ] ) {
4057
+ // prevent missing whitespace between text when block-level
4058
+ // elements are removed.
4059
+ return content + ' ' ;
4060
+ } else {
4061
+ return content ;
4062
+ }
4018
4063
}
4019
4064
} ,
4020
4065
@@ -4056,6 +4101,17 @@ var clipboard = (function() {
4056
4101
4057
4102
shouldKeepNode : function ( nodeName , node ) {
4058
4103
return allowedElements [ nodeName ] && this . hasRequiredAttributes ( nodeName , node ) ;
4104
+ } ,
4105
+
4106
+ cleanWhitespace : function ( str ) {
4107
+ var cleanedStr = str . replace ( / ( .) ( \u00A0 ) / g, function ( match , group1 , group2 , offset , string ) {
4108
+ if ( / [ \u0020 ] / . test ( group1 ) ) {
4109
+ return group1 + '\u00A0' ;
4110
+ } else {
4111
+ return group1 + ' ' ;
4112
+ }
4113
+ } ) ;
4114
+ return cleanedStr ;
4059
4115
}
4060
4116
4061
4117
} ;
@@ -4074,6 +4130,15 @@ var content = (function() {
4074
4130
var zeroWidthNonBreakingSpace = / \uFEFF / g;
4075
4131
4076
4132
return {
4133
+
4134
+ /**
4135
+ * Clean up the Html.
4136
+ */
4137
+ tidyHtml : function ( element ) {
4138
+ this . normalizeTags ( element ) ;
4139
+ } ,
4140
+
4141
+
4077
4142
/**
4078
4143
* Remove empty tags and merge consecutive tags (they must have the same
4079
4144
* attributes).
@@ -4228,27 +4293,6 @@ var content = (function() {
4228
4293
}
4229
4294
} ,
4230
4295
4231
- /**
4232
- * Convert the first and last space to a non breaking space charcter to
4233
- * prevent visual collapse by some browser.
4234
- *
4235
- * @method normalizeSpaces
4236
- * @param {HTMLElement } element The element to process.
4237
- */
4238
- normalizeSpaces : function ( element ) {
4239
- var nonBreakingSpace = '\u00A0' ;
4240
-
4241
- if ( ! element ) return ;
4242
-
4243
- if ( element . nodeType === nodeType . textNode ) {
4244
- element . nodeValue = element . nodeValue . replace ( / ^ ( \s ) / , nonBreakingSpace ) . replace ( / ( \s ) $ / , nonBreakingSpace ) ;
4245
- }
4246
- else {
4247
- this . normalizeSpaces ( element . firstChild ) ;
4248
- this . normalizeSpaces ( element . lastChild ) ;
4249
- }
4250
- } ,
4251
-
4252
4296
/**
4253
4297
* Get all tags that start or end inside the range
4254
4298
*/
@@ -4860,10 +4904,8 @@ var createDefaultBehavior = function(editable) {
4860
4904
}
4861
4905
element . appendChild ( after ) ;
4862
4906
4863
- content . normalizeTags ( newNode ) ;
4864
- content . normalizeSpaces ( newNode ) ;
4865
- content . normalizeTags ( element ) ;
4866
- content . normalizeSpaces ( element ) ;
4907
+ content . tidyHtml ( newNode ) ;
4908
+ content . tidyHtml ( element ) ;
4867
4909
element . focus ( ) ;
4868
4910
} ,
4869
4911
@@ -4894,8 +4936,7 @@ var createDefaultBehavior = function(editable) {
4894
4936
merger . parentNode . removeChild ( merger ) ;
4895
4937
4896
4938
cursor . save ( ) ;
4897
- content . normalizeTags ( container ) ;
4898
- content . normalizeSpaces ( container ) ;
4939
+ content . tidyHtml ( container ) ;
4899
4940
cursor . restore ( ) ;
4900
4941
cursor . setVisibleSelection ( ) ;
4901
4942
} ,
@@ -5189,8 +5230,6 @@ Dispatcher.prototype.setupElementEvents = function() {
5189
5230
} ) . on ( 'paste.editable' , _this . editableSelector , function ( event ) {
5190
5231
log ( 'Paste' ) ;
5191
5232
_this . notify ( 'clipboard' , this , 'paste' , _this . selectionWatcher . getFreshSelection ( ) ) ;
5192
- // todo: this might be a bug since there is a timeout in the paste logic
5193
- // consider moving the paste code in here an not in the default behaviour
5194
5233
_this . triggerChangeEvent ( this ) ;
5195
5234
} ) . on ( 'input.editable' , _this . editableSelector , function ( event ) {
5196
5235
log ( 'Input' ) ;
0 commit comments