Ticket #3401: 3401.patch
File 3401.patch, 21.8 KB (added by , 14 years ago) |
---|
-
_source/plugins/htmlwriter/plugin.js
67 67 68 68 var dtd = CKEDITOR.dtd; 69 69 70 for ( var e in CKEDITOR.tools.extend( {}, dtd.$ block, dtd.$listItem, dtd.$tableContent ) )70 for ( var e in CKEDITOR.tools.extend( {}, dtd.$noneBodyContent, dtd.$block, dtd.$listItem, dtd.$tableContent ) ) 71 71 { 72 72 this.setRules( e, 73 73 { -
_source/core/htmlparser/element.js
35 35 this.children = []; 36 36 37 37 var dtd = CKEDITOR.dtd, 38 isBlockLike = !!( dtd.$ block[ name ] || dtd.$listItem[ name ] || dtd.$tableContent[ name ] ),38 isBlockLike = !!( dtd.$noneBodyContent[ name ] || dtd.$block[ name ] || dtd.$listItem[ name ] || dtd.$tableContent[ name ] ), 39 39 isEmpty = !!dtd.$empty[ name ]; 40 40 41 41 this.isEmpty = isEmpty; -
_source/plugins/wysiwygarea/plugin.js
15 15 */ 16 16 var nonExitableElementNames = { table:1,pre:1 }; 17 17 // Matching an empty paragraph at the end of document. 18 var emptyParagraphRegexp = /\s*<(p|div|address|h\d|center)[^>]*>\s*(?:<br[^>]*>| | )\s*(:?<\/\1>)?\s* $/gi;18 var emptyParagraphRegexp = /\s*<(p|div|address|h\d|center)[^>]*>\s*(?:<br[^>]*>| | )\s*(:?<\/\1>)?\s*(:?$|<\/body>)/gi; 19 19 20 20 function onInsertHtml( evt ) 21 21 { … … 550 550 { 551 551 isLoadingData = true; 552 552 553 // Get the HTML version of the data.553 // Get the full page HTML version of the data. 554 554 if ( editor.dataProcessor ) 555 { 556 data = editor.dataProcessor.toHtml( data, fixForBody ); 557 } 555 data = editor.dataProcessor.toHtml( data, fixForBody, true, editor.config.docType ); 558 556 559 data = 560 editor.config.docType + 561 '<html dir="' + editor.config.contentsLangDirection + '">' + 562 '<head>' + 563 '<link type="text/css" rel="stylesheet" href="' + 564 [].concat( editor.config.contentsCss ).join( '"><link type="text/css" rel="stylesheet" href="' ) + 565 '">' + 566 '<style type="text/css" _fcktemp="true">' + 567 editor._.styles.join( '\n' ) + 568 '</style>'+ 569 '</head>' + 570 '<body>' + 571 data + 572 '</body>' + 573 '</html>' + 574 activationScript; 575 576 window[ '_cke_htmlToLoad_' + editor.name ] = data; 557 window[ '_cke_htmlToLoad_' + editor.name ] = data + activationScript; 577 558 CKEDITOR._[ 'contentDomReady' + editor.name ] = contentDomReady; 578 559 createIFrame(); 579 560 … … 589 570 590 571 getData : function() 591 572 { 592 var data = iframe.getFrameDocument().getBody().getHtml(); 573 var documentElement = iframe.getFrameDocument().getDocumentElement(), 574 docTypeAttr = documentElement.getAttribute( 'cke_docType' ), 575 data = ( docTypeAttr ? decodeURIComponent( docTypeAttr ) : '' ) + documentElement.getOuterHtml(); 593 576 594 577 if ( editor.dataProcessor ) 595 data = editor.dataProcessor.toDataFormat( data, fixForBody );578 data = editor.dataProcessor.toDataFormat( data, fixForBody, true ); 596 579 597 580 // Strip the last blank paragraph within document. 598 581 if ( editor.config.ignoreEmptyParagraph ) … … 635 618 // Auto fixing on some document structure weakness to enhance usabilities. (#3190 and #3189) 636 619 editor.on( 'selectionChange', onSelectionChangeFixBody, null, null, 1 ); 637 620 }); 638 } 639 }); 621 622 function removeInternal( element ) 623 { 624 if( element.attributes.cke_temp ) 625 return false; 626 } 627 628 // Cleanup rules for wysiwyg on fullPage mode. 629 var editorHtmlFilter = editor.dataProcessor.htmlFilter; 630 editorHtmlFilter.addRules( 631 { 632 attributeNames : 633 [ 634 [ 'hidefocus', '' ] 635 ], 636 637 elements : 638 { 639 html : function( element ) 640 { 641 delete element.attributes.cke_doctype; 642 }, 643 body : function( element ) 644 { 645 delete element.attributes.spellcheck; 646 delete element.attributes.contenteditable; 647 }, 648 // Elements used interally in 'wysiwyg' mode shouldn't appear in output. 649 style : removeInternal, 650 link : removeInternal 651 } 652 } ); 653 654 } 655 }); 640 656 })(); 641 657 642 658 /** -
_source/core/dom/element.js
328 328 */ 329 329 getHtml : function() 330 330 { 331 return this.$.innerHTML; 331 var retval = this.$.innerHTML; 332 // Strip <?xml:namespace> tag in the output HTML of 333 // namespaced element in IE(#3341). 334 return CKEDITOR.env.ie ? retval.replace( /<\?[^>]*>/g, '' ) : retval; 332 335 }, 333 336 334 337 getOuterHtml : function() -
_source/core/dtd.js
51 51 N = {'#':1}, 52 52 O = X({param:1},K), 53 53 P = X({form:1},A,D,E,I), 54 Q = {li:1}; 54 Q = {li:1}, 55 R = {style:1,script:1}, 56 S = {base:1,link:1,meta:1,title:1}, 57 T = X(S,R), 58 U = {head:1,body:1}, 59 V = {html:1}; 55 60 56 var block = {address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1}; 61 var block = {address:1,blockquote:1,center:1,dir:1,div:1,dl:1,fieldset:1,form:1,h1:1,h2:1,h3:1,h4:1,h5:1,h6:1,hr:1,isindex:1,menu:1,noframes:1,ol:1,p:1,pre:1,table:1,ul:1}, 62 inline = {a:1,abbr:1,acronym:1,b:1,basefont:1,bdo:1,big:1,br:1,cite:1,code:1,dfn:1,em:1,font:1,i:1,img:1,input:1,kbd:1,label:1,q:1,s:1,samp:1,select:1,small:1,span:1,strike:1,strong:1,sub:1,sup:1,textarea:1,tt:1,u:1,'var':1,applet:1,button:1,del:1,iframe:1,ins:1,map:1,object:1,script:1}; 57 63 58 64 return /** @lends CKEDITOR.dtd */ { 59 65 60 66 // The "$" items have been added manually. 61 67 62 /** 68 /** 69 * The document element. 70 */ 71 $ : V, 72 73 // List of elements won't appear under body. 74 $noneBodyContent: X(V,U,S), 75 76 /** 63 77 * List of block elements, like "p" or "div". 64 78 * @type Object 65 79 * @example 66 80 */ 67 81 $block : block, 68 82 69 $body : X({script:1}, block),83 $inline : inline, 70 84 85 $body : X({script:1,style:1}, block), 86 71 87 $cdata : {script:1,style:1}, 72 88 73 89 /** … … 120 136 */ 121 137 $tableContent : {caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1}, 122 138 123 col : {}, 139 html: U, 140 head: T, 141 style: N, 142 script: N, 143 body: X(inline, block), 144 col : {}, 124 145 tr : {td:1,th:1}, 125 146 img : {}, 126 147 colgroup : {col:1}, … … 200 221 pre : X(G,C), 201 222 p : L, 202 223 em : L, 203 dfn : L 224 dfn : L, 225 base: {}, 226 link: {}, 227 meta: {}, 228 title: N 204 229 }; 205 230 })(); 206 231 -
_source/core/htmlparser/basicwriter.js
15 15 16 16 proto : 17 17 { 18 docType : function( docType ) 19 { 20 this._.output.push( docType ); 21 }, 22 18 23 /** 19 24 * Writes the tag opening part for a opener tag. 20 25 * @param {String} tagName The element name for this tag. … … 117 122 reset : function() 118 123 { 119 124 this._.output = []; 125 this._.indent = false; 120 126 }, 121 127 122 128 /** -
_source/core/htmlparser/fragment.js
18 18 * alert( fragment.children.length ); "2" 19 19 */ 20 20 this.children = []; 21 this.attributes = {}; 21 22 22 23 /** 23 24 * Get the fragment parent. Should always be null. … … 30 31 /** @private */ 31 32 this._ = 32 33 { 34 fullPage : true, 35 docType : '', 33 36 isBlockLike : true, 34 37 hasInlineStarted : false 35 38 }; … … 114 117 elementName = realElementName; 115 118 else 116 119 elementName = element.name; 117 if ( !( elementName in CKEDITOR.dtd.$body ) ) 120 if ( elementName 121 && !( elementName in CKEDITOR.dtd.$body ) 122 && !( elementName in CKEDITOR.dtd.$noneBodyContent ) ) 118 123 { 119 124 var savedCurrent = currentNode; 120 125 … … 156 161 } 157 162 } 158 163 164 parser.onDocType = function ( docType ) 165 { 166 fragment._.docType = docType; 167 }; 168 159 169 parser.onTagOpen = function( tagName, attributes, selfClosing ) 160 170 { 161 171 var element = new CKEDITOR.htmlParser.element( tagName, attributes ); … … 180 190 } 181 191 182 192 var currentName = currentNode.name, 183 currentDtd = ( currentName && CKEDITOR.dtd[ currentName ] ) || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ); 193 currentDtd = currentName && 194 ( CKEDITOR.dtd[ currentName ] 195 || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) ); 184 196 185 197 // If the element cannot be child of the current element. 186 if ( !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] ) 198 if ( currentDtd // Fragment could receive any elements. 199 && !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] ) 187 200 { 188 // If this is the fragment node, just ignore this tag and add189 // its children.190 if ( !currentName )191 return;192 201 193 202 var reApply = false, 194 203 addPoint; // New position to start adding nodes. … … 330 339 index--; 331 340 } 332 341 } 342 343 if( tagName == 'body' ) 344 fixForBody = false; 333 345 }; 334 346 335 347 parser.onText = function( text ) … … 345 357 346 358 checkPending(); 347 359 348 if ( fixForBody && !currentNode.type ) 360 if ( fixForBody 361 && CKEDITOR.tools.trim( text ) 362 && ( !currentNode.type || currentNode.name == 'body' ) ) 349 363 this.onTagOpen( fixForBody, {} ); 350 364 351 365 // Shrinking consequential spaces into one single for all elements … … 375 389 var parent = currentNode.parent, 376 390 node = currentNode; 377 391 378 if ( fixForBody && !parent.type && !CKEDITOR.dtd.$body[ node.name ] ) 392 if ( fixForBody 393 && ( !parent.type || parent.name == 'body' ) 394 && !CKEDITOR.dtd.$body[ node.name ] ) 379 395 { 380 396 currentNode = parent; 381 397 parser.onTagOpen( fixForBody, {} ); … … 444 460 */ 445 461 writeHtml : function( writer, filter ) 446 462 { 463 // Call element filter on fragment as well as write doc-type. 464 if( !this.name ) 465 { 466 filter && filter.onElement( this ); 467 this._.docType && writer.docType( this._.docType ); 468 } 469 447 470 for ( var i = 0, len = this.children.length ; i < len ; i++ ) 448 471 this.children[i].writeHtml( writer, filter ); 449 472 } -
_source/core/htmlparser/filter.js
127 127 128 128 function addItemsToList( list, items, priority ) 129 129 { 130 if( typeof items == 'function' ) 131 items = [ items ]; 132 130 133 var i, j, 131 134 listLength = list.length, 132 135 itemsLength = items && items.length; -
_source/plugins/htmldataprocessor/plugin.js
11 11 12 12 var protectedSourceMarker = '{cke_protected}'; 13 13 14 function childByTagName( element, tagName ) 15 { 16 var children = element.children, 17 child; 18 for ( var i = 0; i < children.length; i++ ) 19 { 20 child = children[ i ]; 21 if( child.name && child.name == tagName ) 22 return child; 23 } 24 } 14 25 15 26 // Return the last non-space child node of the block (#4344). 16 27 function lastNoneSpaceChild( block ) … … 66 77 block.add( new CKEDITOR.htmlParser.text( '\xa0' ) ); 67 78 } 68 79 80 function wrapContent( element, tagName ) 81 { 82 var childrens = element.children, 83 root = new CKEDITOR.htmlParser.element( tagName, {} ); 84 element.children = []; 85 element.add( root ); 86 for ( var i = 0; i < childrens.length; i++ ) 87 { 88 root.add( childrens[ i ] ); 89 } 90 return root; 91 } 92 69 93 var dtd = CKEDITOR.dtd; 70 94 71 95 // Find out the list of block-like tags that can contain <br>. … … 206 230 return html.replace( protectAttributeRegex, '$& _cke_saved_$1' ); 207 231 } 208 232 209 var protectStyleTagsRegex = /<(style)(?=[ >])[^>]*>[^<]*<\/\1>/gi; 233 var protectStyleTagsRegex = /<(style)(?=[ >])[^>]*>[^<]*<\/\1>/gi, 234 protectFullPageElementsRegex = /<(:?link|meta|base).*?>/; 210 235 var encodedTagsRegex = /<cke:encoded>([^<]*)<\/cke:encoded>/gi; 211 236 var protectElementNamesRegex = /(<\/?)((?:object|embed|param).*?>)/gi; 237 var shelvefullPageElementsRegex = /(<\/?)((?:html|body|head|title).*?>)/gi, 238 shelvedElementsRegex = /(<\/?)cke_fullpage:([^>]*>)/gi; 212 239 var protectSelfClosingRegex = /<cke:param(.*?)\/>/gi; 213 240 214 function protectStyleTagsMatch( match )241 function encodeMatched( match ) 215 242 { 216 243 return '<cke:encoded>' + encodeURIComponent( match ) + '</cke:encoded>'; 217 244 } 218 245 246 function shelveTags( html, isUnShelve ) 247 { 248 if ( isUnShelve ) 249 return unprotectEncodedTags( html.replace( shelvedElementsRegex, '$1$2' ) ); 250 else 251 { 252 return html 253 .replace( shelvefullPageElementsRegex, '$1cke_fullpage:$2' ) 254 .replace( protectFullPageElementsRegex, encodeMatched ); 255 } 256 } 257 219 258 function protectStyleTags( html ) 220 259 { 221 return html.replace( protectStyleTagsRegex, protectStyleTagsMatch);260 return html.replace( protectStyleTagsRegex, encodeMatched ); 222 261 } 223 262 function protectElementsNames( html ) 224 263 { … … 291 330 292 331 dataProcessor.writer.forceSimpleAmpersand = editor.config.forceSimpleAmpersand; 293 332 333 var fullPageDataFilterRules = 334 { 335 // Full page support. (#4067) 336 elements : 337 { 338 $ : function( element ) 339 { 340 // Fix fullPage elements at the fragment element. 341 if ( ( element._.fullPage !== false ) && !element.name ) 342 { 343 var firstChild = element.children[ 0 ]; 344 // Full document. 345 if ( firstChild && firstChild.name == 'html' ) 346 return; 347 // Missing <html>. 348 if ( !firstChild || firstChild.name in dtd.html ) 349 wrapContent( element, 'html' ); 350 // Missing <html> and <body>/<head>. 351 else 352 { 353 wrapContent( wrapContent( element, 'html' ), 354 firstChild.name in dtd.head ? 'head' : 'body' ); 355 } 356 } 357 }, 358 359 html : function ( element ) 360 { 361 // Both <head> and <body> are mandatory. 362 if ( !childByTagName( element, 'head' ) ) 363 element.children.splice(0, 0, new CKEDITOR.htmlParser.element('head', {})); 364 if ( !childByTagName( element, 'body' ) ) 365 element.children.splice( element.children.length, 0, new CKEDITOR.htmlParser.element( 'body', {} ) ); 366 367 // 1. Save doc-type as a custom attribute; 368 // 2. Adding missing xml namespace attribute; 369 // 3. Adding missing language direction attribute. 370 var attrs = element.attributes, 371 docType = element.parent._.docType, 372 configDir = editor.config.contentsLangDirection; 373 if ( docType ) 374 { 375 attrs.cke_docType = encodeURIComponent(docType); 376 docType.match(/xhtml/i) 377 && !attrs.xmlns 378 && ( attrs.xmlns = 'http://www.w3.org/1999/xhtml' ); 379 } 380 !attrs.dir && configDir && ( attrs.dir = configDir ); 381 }, 382 383 head : function ( element ) 384 { 385 // <title> is mandatory. 386 if ( !childByTagName( element, 'title' ) ) 387 element.children.splice( 0, 0, new CKEDITOR.htmlParser.element( 'title' , {} ) ); 388 389 // Base href is mandatory. 390 if( editor.config.baseHref && !childByTagName( element, 'base' ) ) 391 element.children.splice( 0, 0, 392 new CKEDITOR.htmlParser.element( 'base', 393 { 394 href : editor.config.baseHref 395 } ) ); 396 397 // Adding default styles. 398 var styleLinks = [].concat(editor.config.contentsCss), 399 styleText = editor._.styles.join('\n'); 400 401 if ( styleLinks ) 402 { 403 for ( var i = 0; i < styleLinks.length; i++ ) 404 { 405 var link = new CKEDITOR.htmlParser.element('link', 406 { 407 type: 'text/css' , 408 rel : 'stylesheet', 409 href : styleLinks[ i ], 410 cke_temp : 1 411 }); 412 element.add(link); 413 } 414 } 415 if ( styleText ) 416 { 417 var style = new CKEDITOR.htmlParser.element('style', 418 { 419 type: 'text/css', 420 cke_temp : 1 421 }); 422 style.add(new CKEDITOR.htmlParser.text(styleText)); 423 element.add(style); 424 } 425 } 426 } 427 }; 428 429 var fullPageHtmlFilterRules = 430 { 431 elements : 432 { 433 html : function ( element ) 434 { 435 // Preserve only body contents in non-fullPage mode. 436 if( element.parent._.fullPage === false ) 437 { 438 var children = element.children, 439 contents, 440 child; 441 for ( var i = 0; i < children.length; i++ ) 442 { 443 child = children[ i ]; 444 if ( 'body' == child.name ) 445 { 446 contents = child.children; 447 break; 448 } 449 } 450 451 element.children = contents; 452 delete element.name; 453 } 454 } 455 } 456 }; 457 294 458 dataProcessor.dataFilter.addRules( defaultDataFilterRules ); 295 459 dataProcessor.dataFilter.addRules( defaultDataBlockFilterRules ); 460 dataProcessor.dataFilter.addRules( fullPageDataFilterRules ); 296 461 dataProcessor.htmlFilter.addRules( defaultHtmlFilterRules ); 297 462 dataProcessor.htmlFilter.addRules( defaultHtmlBlockFilterRules ); 463 dataProcessor.htmlFilter.addRules( fullPageHtmlFilterRules ); 298 464 } 299 465 }); 300 466 … … 309 475 310 476 CKEDITOR.htmlDataProcessor.prototype = 311 477 { 312 toHtml : function( data, fixForBody )478 toHtml : function( data, fixForBody, fullPage, docType ) 313 479 { 314 480 // The source data is already HTML, but we need to clean 315 481 // it up and apply the filter. … … 333 499 // protecting them into open-close.(#3591) 334 500 data = protectSelfClosingElements( data ); 335 501 502 data = shelveTags( data ); 336 503 // Call the browser to help us fixing a possibly invalid HTML 337 504 // structure. 338 var div = document.createElement( 'div' );505 var div = new CKEDITOR.dom.element( 'div' ); 339 506 // Add fake character to workaround IE comments bug. (#3801) 340 div. innerHTML = 'a' + data;341 data = div. innerHTML.substr( 1 );507 div.setHtml( 'a' + data ); 508 data = div.getHtml().substr( 1 ); 342 509 510 data = shelveTags( data, true ); 343 511 if ( CKEDITOR.env.ie ) 344 512 data = unprotectEncodedTags( data ); 345 513 … … 348 516 var fragment = CKEDITOR.htmlParser.fragment.fromHtml( data, fixForBody ), 349 517 writer = new CKEDITOR.htmlParser.basicWriter(); 350 518 519 // Is docType missing and been explicitly defined in configuration? 520 docType && !fragment._.docType && ( fragment._.docType = docType ); 521 522 // Is HTML snippet mode instead of full page ? 523 ( fullPage !== true ) 524 && ( fragment._.fullPage = false, delete fragment._.docType ); 525 351 526 fragment.writeHtml( writer, this.dataFilter ); 352 527 353 528 return writer.getHtml( true ); 354 529 }, 355 530 356 toDataFormat : function( html, fixForBody )531 toDataFormat : function( html, fixForBody, fullPage ) 357 532 { 358 533 var writer = this.writer, 359 534 fragment = CKEDITOR.htmlParser.fragment.fromHtml( html, fixForBody ); 360 535 361 writer.reset(); 536 // Is HTML snippet mode instead of full page ? 537 ( fullPage !== true ) 538 && ( fragment._.fullPage = false, delete fragment._.docType ); 362 539 540 writer.reset(); 363 541 fragment.writeHtml( writer, this.htmlFilter ); 364 542 365 543 return writer.getHtml( true ); -
_source/core/htmlparser.js
12 12 { 13 13 this._ = 14 14 { 15 htmlPartsRegex : new RegExp( '<(?:(?:\\/([^>]+)>)|(?:!--([\\S|\\s]*?)-->)|(?:([^\\s>]+)\\s*((?:(?:[^"\'>]+)|(?:"[^"]*")|(?:\'[^\']*\'))*)\\/?>))', 'g' ) 15 htmlPartsRegex : new RegExp( '<(?:(?:\\/([^>]+)>)|(?:!--([\\S|\\s]*?)-->)|(?:([^\\s>]+)\\s*((?:(?:[^"\'>]+)|(?:"[^"]*")|(?:\'[^\']*\'))*)\\/?>))', 'g' ), 16 docTypeRegex : /<!DOCTYPE[^>]*>/i 16 17 }; 17 18 }; 18 19 … … 24 25 CKEDITOR.htmlParser.prototype = 25 26 { 26 27 /** 28 * Function to be fired when a docType defintion is found. This function 29 * should be overriden when using this class. 30 * @param {String} docType The declaration text. 31 */ 32 onDocType : function() {}, 33 34 /** 27 35 * Function to be fired when a tag opener is found. This function 28 36 * should be overriden when using this class. 29 37 * @param {String} tagName The tag name. The name is guarantted to be … … 113 121 */ 114 122 parse : function( html ) 115 123 { 124 // Remove docType declaration at first. 125 var self = this; 126 html = html.replace( this._.docTypeRegex, function( match ) 127 { 128 self.onDocType( match ); 129 return ''; 130 } ); 131 116 132 var parts, 117 133 tagName, 118 134 nextIndex = 0,