Index: _source/plugins/htmlwriter/plugin.js
===================================================================
--- _source/plugins/htmlwriter/plugin.js (revision 3676)
+++ _source/plugins/htmlwriter/plugin.js Fri Oct 23 16:13:02 CST 2009
@@ -67,7 +67,7 @@
var dtd = CKEDITOR.dtd;
- for ( var e in CKEDITOR.tools.extend( {}, dtd.$block, dtd.$listItem, dtd.$tableContent ) )
+ for ( var e in CKEDITOR.tools.extend( {}, dtd.$noneBodyContent, dtd.$block, dtd.$listItem, dtd.$tableContent ) )
{
this.setRules( e,
{
Index: _source/core/htmlparser/element.js
===================================================================
--- _source/core/htmlparser/element.js (revision 3654)
+++ _source/core/htmlparser/element.js Fri Oct 23 16:13:02 CST 2009
@@ -35,7 +35,7 @@
this.children = [];
var dtd = CKEDITOR.dtd,
- isBlockLike = !!( dtd.$block[ name ] || dtd.$listItem[ name ] || dtd.$tableContent[ name ] ),
+ isBlockLike = !!( dtd.$noneBodyContent[ name ] || dtd.$block[ name ] || dtd.$listItem[ name ] || dtd.$tableContent[ name ] ),
isEmpty = !!dtd.$empty[ name ];
this.isEmpty = isEmpty;
Index: _source/plugins/wysiwygarea/plugin.js
===================================================================
--- _source/plugins/wysiwygarea/plugin.js (revision 4371)
+++ _source/plugins/wysiwygarea/plugin.js Fri Oct 23 17:54:58 CST 2009
@@ -15,7 +15,7 @@
*/
var nonExitableElementNames = { table:1,pre:1 };
// Matching an empty paragraph at the end of document.
- var emptyParagraphRegexp = /\s*<(p|div|address|h\d|center)[^>]*>\s*(?:
]*>| | )\s*(:?<\/\1>)?\s*$/gi;
+ var emptyParagraphRegexp = /\s*<(p|div|address|h\d|center)[^>]*>\s*(?:
]*>| | )\s*(:?<\/\1>)?\s*(:?$|<\/body>)/gi;
function onInsertHtml( evt )
{
@@ -550,30 +550,11 @@
{
isLoadingData = true;
- // Get the HTML version of the data.
+ // Get the full page HTML version of the data.
if ( editor.dataProcessor )
- {
- data = editor.dataProcessor.toHtml( data, fixForBody );
- }
+ data = editor.dataProcessor.toHtml( data, fixForBody, true, editor.config.docType );
- data =
- editor.config.docType +
- '' +
- '
' +
- '' +
- ''+
- '' +
- '' +
- data +
- '' +
- '' +
- activationScript;
-
- window[ '_cke_htmlToLoad_' + editor.name ] = data;
+ window[ '_cke_htmlToLoad_' + editor.name ] = data + activationScript;
CKEDITOR._[ 'contentDomReady' + editor.name ] = contentDomReady;
createIFrame();
@@ -589,10 +570,12 @@
getData : function()
{
- var data = iframe.getFrameDocument().getBody().getHtml();
+ var documentElement = iframe.getFrameDocument().getDocumentElement(),
+ docTypeAttr = documentElement.getAttribute( 'cke_docType' ),
+ data = ( docTypeAttr ? decodeURIComponent( docTypeAttr ) : '' ) + documentElement.getOuterHtml();
if ( editor.dataProcessor )
- data = editor.dataProcessor.toDataFormat( data, fixForBody );
+ data = editor.dataProcessor.toDataFormat( data, fixForBody, true );
// Strip the last blank paragraph within document.
if ( editor.config.ignoreEmptyParagraph )
@@ -635,8 +618,41 @@
// Auto fixing on some document structure weakness to enhance usabilities. (#3190 and #3189)
editor.on( 'selectionChange', onSelectionChangeFixBody, null, null, 1 );
});
+
+ function removeInternal( element )
+ {
+ if( element.attributes.cke_temp )
+ return false;
- }
+ }
+
+ // Cleanup rules for wysiwyg on fullPage mode.
+ var editorHtmlFilter = editor.dataProcessor.htmlFilter;
+ editorHtmlFilter.addRules(
+ {
+ attributeNames :
+ [
+ [ 'hidefocus', '' ]
+ ],
+
+ elements :
+ {
+ html : function( element )
+ {
+ delete element.attributes.cke_doctype;
+ },
+ body : function( element )
+ {
+ delete element.attributes.spellcheck;
+ delete element.attributes.contenteditable;
+ },
+ // Elements used interally in 'wysiwyg' mode shouldn't appear in output.
+ style : removeInternal,
+ link : removeInternal
+ }
- });
+ } );
+
+ }
+ });
})();
/**
Index: _source/core/dom/element.js
===================================================================
--- _source/core/dom/element.js (revision 4196)
+++ _source/core/dom/element.js Fri Oct 23 16:13:02 CST 2009
@@ -328,7 +328,10 @@
*/
getHtml : function()
{
- return this.$.innerHTML;
+ var retval = this.$.innerHTML;
+ // Strip tag in the output HTML of
+ // namespaced element in IE(#3341).
+ return CKEDITOR.env.ie ? retval.replace( /<\?[^>]*>/g, '' ) : retval;
},
getOuterHtml : function()
Index: _source/core/dtd.js
===================================================================
--- _source/core/dtd.js (revision 4320)
+++ _source/core/dtd.js Fri Oct 23 16:13:02 CST 2009
@@ -51,23 +51,39 @@
N = {'#':1},
O = X({param:1},K),
P = X({form:1},A,D,E,I),
- Q = {li:1};
+ Q = {li:1},
+ R = {style:1,script:1},
+ S = {base:1,link:1,meta:1,title:1},
+ T = X(S,R),
+ U = {head:1,body:1},
+ V = {html:1};
- 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};
+ 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},
+ 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};
return /** @lends CKEDITOR.dtd */ {
// The "$" items have been added manually.
- /**
+ /**
+ * The document element.
+ */
+ $ : V,
+
+ // List of elements won't appear under body.
+ $noneBodyContent: X(V,U,S),
+
+ /**
* List of block elements, like "p" or "div".
* @type Object
* @example
*/
$block : block,
- $body : X({script:1}, block),
+ $inline : inline,
+ $body : X({script:1,style:1}, block),
+
$cdata : {script:1,style:1},
/**
@@ -120,7 +136,12 @@
*/
$tableContent : {caption:1,col:1,colgroup:1,tbody:1,td:1,tfoot:1,th:1,thead:1,tr:1},
+ html: U,
+ head: T,
+ style: N,
+ script: N,
+ body: X(inline, block),
- col : {},
+ col : {},
tr : {td:1,th:1},
img : {},
colgroup : {col:1},
@@ -200,7 +221,11 @@
pre : X(G,C),
p : L,
em : L,
- dfn : L
+ dfn : L,
+ base: {},
+ link: {},
+ meta: {},
+ title: N
};
})();
Index: _source/core/htmlparser/basicwriter.js
===================================================================
--- _source/core/htmlparser/basicwriter.js (revision 3308)
+++ _source/core/htmlparser/basicwriter.js Fri Oct 23 16:13:02 CST 2009
@@ -15,6 +15,11 @@
proto :
{
+ docType : function( docType )
+ {
+ this._.output.push( docType );
+ },
+
/**
* Writes the tag opening part for a opener tag.
* @param {String} tagName The element name for this tag.
@@ -117,6 +122,7 @@
reset : function()
{
this._.output = [];
+ this._.indent = false;
},
/**
Index: _source/core/htmlparser/fragment.js
===================================================================
--- _source/core/htmlparser/fragment.js (revision 4373)
+++ _source/core/htmlparser/fragment.js Fri Oct 23 17:43:28 CST 2009
@@ -18,6 +18,7 @@
* alert( fragment.children.length ); "2"
*/
this.children = [];
+ this.attributes = {};
/**
* Get the fragment parent. Should always be null.
@@ -30,6 +31,8 @@
/** @private */
this._ =
{
+ fullPage : true,
+ docType : '',
isBlockLike : true,
hasInlineStarted : false
};
@@ -114,7 +117,9 @@
elementName = realElementName;
else
elementName = element.name;
- if ( !( elementName in CKEDITOR.dtd.$body ) )
+ if ( elementName
+ && !( elementName in CKEDITOR.dtd.$body )
+ && !( elementName in CKEDITOR.dtd.$noneBodyContent ) )
{
var savedCurrent = currentNode;
@@ -156,6 +161,11 @@
}
}
+ parser.onDocType = function ( docType )
+ {
+ fragment._.docType = docType;
+ };
+
parser.onTagOpen = function( tagName, attributes, selfClosing )
{
var element = new CKEDITOR.htmlParser.element( tagName, attributes );
@@ -180,15 +190,14 @@
}
var currentName = currentNode.name,
- currentDtd = ( currentName && CKEDITOR.dtd[ currentName ] ) || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span );
+ currentDtd = currentName &&
+ ( CKEDITOR.dtd[ currentName ]
+ || ( currentNode._.isBlockLike ? CKEDITOR.dtd.div : CKEDITOR.dtd.span ) );
// If the element cannot be child of the current element.
- if ( !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] )
+ if ( currentDtd // Fragment could receive any elements.
+ && !element.isUnknown && !currentNode.isUnknown && !currentDtd[ tagName ] )
{
- // If this is the fragment node, just ignore this tag and add
- // its children.
- if ( !currentName )
- return;
var reApply = false,
addPoint; // New position to start adding nodes.
@@ -330,6 +339,9 @@
index--;
}
}
+
+ if( tagName == 'body' )
+ fixForBody = false;
};
parser.onText = function( text )
@@ -345,7 +357,9 @@
checkPending();
- if ( fixForBody && !currentNode.type )
+ if ( fixForBody
+ && CKEDITOR.tools.trim( text )
+ && ( !currentNode.type || currentNode.name == 'body' ) )
this.onTagOpen( fixForBody, {} );
// Shrinking consequential spaces into one single for all elements
@@ -375,7 +389,9 @@
var parent = currentNode.parent,
node = currentNode;
- if ( fixForBody && !parent.type && !CKEDITOR.dtd.$body[ node.name ] )
+ if ( fixForBody
+ && ( !parent.type || parent.name == 'body' )
+ && !CKEDITOR.dtd.$body[ node.name ] )
{
currentNode = parent;
parser.onTagOpen( fixForBody, {} );
@@ -444,6 +460,13 @@
*/
writeHtml : function( writer, filter )
{
+ // Call element filter on fragment as well as write doc-type.
+ if( !this.name )
+ {
+ filter && filter.onElement( this );
+ this._.docType && writer.docType( this._.docType );
+ }
+
for ( var i = 0, len = this.children.length ; i < len ; i++ )
this.children[i].writeHtml( writer, filter );
}
Index: _source/core/htmlparser/filter.js
===================================================================
--- _source/core/htmlparser/filter.js (revision 3309)
+++ _source/core/htmlparser/filter.js Fri Oct 23 16:13:02 CST 2009
@@ -127,6 +127,9 @@
function addItemsToList( list, items, priority )
{
+ if( typeof items == 'function' )
+ items = [ items ];
+
var i, j,
listLength = list.length,
itemsLength = items && items.length;
Index: _source/plugins/htmldataprocessor/plugin.js
===================================================================
--- _source/plugins/htmldataprocessor/plugin.js (revision 4373)
+++ _source/plugins/htmldataprocessor/plugin.js Fri Oct 23 17:48:32 CST 2009
@@ -11,6 +11,17 @@
var protectedSourceMarker = '{cke_protected}';
+ function childByTagName( element, tagName )
+ {
+ var children = element.children,
+ child;
+ for ( var i = 0; i < children.length; i++ )
+ {
+ child = children[ i ];
+ if( child.name && child.name == tagName )
+ return child;
+ }
+ }
// Return the last non-space child node of the block (#4344).
function lastNoneSpaceChild( block )
@@ -66,6 +77,19 @@
block.add( new CKEDITOR.htmlParser.text( '\xa0' ) );
}
+ function wrapContent( element, tagName )
+ {
+ var childrens = element.children,
+ root = new CKEDITOR.htmlParser.element( tagName, {} );
+ element.children = [];
+ element.add( root );
+ for ( var i = 0; i < childrens.length; i++ )
+ {
+ root.add( childrens[ i ] );
+ }
+ return root;
+ }
+
var dtd = CKEDITOR.dtd;
// Find out the list of block-like tags that can contain
.
@@ -206,19 +230,34 @@
return html.replace( protectAttributeRegex, '$& _cke_saved_$1' );
}
- var protectStyleTagsRegex = /<(style)(?=[ >])[^>]*>[^<]*<\/\1>/gi;
+ var protectStyleTagsRegex = /<(style)(?=[ >])[^>]*>[^<]*<\/\1>/gi,
+ protectFullPageElementsRegex = /<(:?link|meta|base).*?>/;
var encodedTagsRegex = /([^<]*)<\/cke:encoded>/gi;
var protectElementNamesRegex = /(<\/?)((?:object|embed|param).*?>)/gi;
+ var shelvefullPageElementsRegex = /(<\/?)((?:html|body|head|title).*?>)/gi,
+ shelvedElementsRegex = /(<\/?)cke_fullpage:([^>]*>)/gi;
var protectSelfClosingRegex = //gi;
- function protectStyleTagsMatch( match )
+ function encodeMatched( match )
{
return '' + encodeURIComponent( match ) + '';
}
+ function shelveTags( html, isUnShelve )
+ {
+ if ( isUnShelve )
+ return unprotectEncodedTags( html.replace( shelvedElementsRegex, '$1$2' ) );
+ else
+ {
+ return html
+ .replace( shelvefullPageElementsRegex, '$1cke_fullpage:$2' )
+ .replace( protectFullPageElementsRegex, encodeMatched );
+ }
+ }
+
function protectStyleTags( html )
{
- return html.replace( protectStyleTagsRegex, protectStyleTagsMatch );
+ return html.replace( protectStyleTagsRegex, encodeMatched );
}
function protectElementsNames( html )
{
@@ -291,10 +330,129 @@
dataProcessor.writer.forceSimpleAmpersand = editor.config.forceSimpleAmpersand;
+ var fullPageDataFilterRules =
+ {
+ // Full page support. (#4067)
+ elements :
+ {
+ $ : function( element )
+ {
+ // Fix fullPage elements at the fragment element.
+ if ( ( element._.fullPage !== false ) && !element.name )
+ {
+ var firstChild = element.children[ 0 ];
+ // Full document.
+ if ( firstChild && firstChild.name == 'html' )
+ return;
+ // Missing .
+ if ( !firstChild || firstChild.name in dtd.html )
+ wrapContent( element, 'html' );
+ // Missing and /.
+ else
+ {
+ wrapContent( wrapContent( element, 'html' ),
+ firstChild.name in dtd.head ? 'head' : 'body' );
+ }
+ }
+ },
+
+ html : function ( element )
+ {
+ // Both and are mandatory.
+ if ( !childByTagName( element, 'head' ) )
+ element.children.splice(0, 0, new CKEDITOR.htmlParser.element('head', {}));
+ if ( !childByTagName( element, 'body' ) )
+ element.children.splice( element.children.length, 0, new CKEDITOR.htmlParser.element( 'body', {} ) );
+
+ // 1. Save doc-type as a custom attribute;
+ // 2. Adding missing xml namespace attribute;
+ // 3. Adding missing language direction attribute.
+ var attrs = element.attributes,
+ docType = element.parent._.docType,
+ configDir = editor.config.contentsLangDirection;
+ if ( docType )
+ {
+ attrs.cke_docType = encodeURIComponent(docType);
+ docType.match(/xhtml/i)
+ && !attrs.xmlns
+ && ( attrs.xmlns = 'http://www.w3.org/1999/xhtml' );
+ }
+ !attrs.dir && configDir && ( attrs.dir = configDir );
+ },
+
+ head : function ( element )
+ {
+ // is mandatory.
+ if ( !childByTagName(element, 'title') )
+ element.children.splice(0, 0, new CKEDITOR.htmlParser.element('title', {}));
+
+ // Adding default styles.
+ var styleLinks = [].concat(editor.config.contentsCss),
+ styleText = editor._.styles.join('\n');
+
+ if ( styleLinks )
+ {
+ for ( var i = 0; i < styleLinks.length; i++ )
+ {
+ var link = new CKEDITOR.htmlParser.element('link',
+ {
+ type: 'text/css' ,
+ rel : 'stylesheet',
+ href : styleLinks[ i ],
+ cke_temp : 1
+ });
+ element.add(link);
+ }
+ }
+ if ( styleText )
+ {
+ var style = new CKEDITOR.htmlParser.element('style',
+ {
+ type: 'text/css',
+ cke_temp : 1
+ });
+ style.add(new CKEDITOR.htmlParser.text(styleText));
+ element.add(style);
+ }
+ }
+ }
+ };
+
+ var fullPageHtmlFilterRules =
+ {
+ elements :
+ {
+ html : function ( element )
+ {
+ // Preserve only body contents in non-fullPage mode.
+ if( element.parent._.fullPage === false )
+ {
+ var children = element.children,
+ contents,
+ child;
+ for ( var i = 0; i < children.length; i++ )
+ {
+ child = children[ i ];
+ if ( 'body' == child.name )
+ {
+ contents = child.children;
+ break;
+ }
+ }
+
+ element.children = contents;
+ delete element.name;
+ }
+ }
+ }
+ };
+
dataProcessor.dataFilter.addRules( defaultDataFilterRules );
dataProcessor.dataFilter.addRules( defaultDataBlockFilterRules );
+ dataProcessor.dataFilter.addRules( fullPageDataFilterRules );
dataProcessor.htmlFilter.addRules( defaultHtmlFilterRules );
dataProcessor.htmlFilter.addRules( defaultHtmlBlockFilterRules );
+ dataProcessor.htmlFilter.addRules( fullPageHtmlFilterRules );
}
});
@@ -309,7 +467,7 @@
CKEDITOR.htmlDataProcessor.prototype =
{
- toHtml : function( data, fixForBody )
+ toHtml : function( data, fixForBody, fullPage, docType )
{
// The source data is already HTML, but we need to clean
// it up and apply the filter.
@@ -333,13 +491,15 @@
// protecting them into open-close.(#3591)
data = protectSelfClosingElements( data );
+ data = shelveTags( data );
// Call the browser to help us fixing a possibly invalid HTML
// structure.
- var div = document.createElement( 'div' );
+ var div = new CKEDITOR.dom.element( 'div' );
// Add fake character to workaround IE comments bug. (#3801)
- div.innerHTML = 'a' + data;
- data = div.innerHTML.substr( 1 );
+ div.setHtml( 'a' + data );
+ data = div.getHtml().substr( 1 );
+ data = shelveTags( data, true );
if ( CKEDITOR.env.ie )
data = unprotectEncodedTags( data );
@@ -348,18 +508,28 @@
var fragment = CKEDITOR.htmlParser.fragment.fromHtml( data, fixForBody ),
writer = new CKEDITOR.htmlParser.basicWriter();
+ // Is docType missing and been explicitly defined in configuration?
+ docType && !fragment._.docType && ( fragment._.docType = docType );
+
+ // Is HTML snippet mode instead of full page ?
+ ( fullPage !== true )
+ && ( fragment._.fullPage = false, delete fragment._.docType );
+
fragment.writeHtml( writer, this.dataFilter );
return writer.getHtml( true );
},
- toDataFormat : function( html, fixForBody )
+ toDataFormat : function( html, fixForBody, fullPage )
{
var writer = this.writer,
fragment = CKEDITOR.htmlParser.fragment.fromHtml( html, fixForBody );
- writer.reset();
+ // Is HTML snippet mode instead of full page ?
+ ( fullPage !== true )
+ && ( fragment._.fullPage = false, delete fragment._.docType );
+ writer.reset();
fragment.writeHtml( writer, this.htmlFilter );
return writer.getHtml( true );
Index: _source/core/htmlparser.js
===================================================================
--- _source/core/htmlparser.js (revision 4349)
+++ _source/core/htmlparser.js Fri Oct 23 16:13:02 CST 2009
@@ -12,7 +12,8 @@
{
this._ =
{
- htmlPartsRegex : new RegExp( '<(?:(?:\\/([^>]+)>)|(?:!--([\\S|\\s]*?)-->)|(?:([^\\s>]+)\\s*((?:(?:[^"\'>]+)|(?:"[^"]*")|(?:\'[^\']*\'))*)\\/?>))', 'g' )
+ htmlPartsRegex : new RegExp( '<(?:(?:\\/([^>]+)>)|(?:!--([\\S|\\s]*?)-->)|(?:([^\\s>]+)\\s*((?:(?:[^"\'>]+)|(?:"[^"]*")|(?:\'[^\']*\'))*)\\/?>))', 'g' ),
+ docTypeRegex : /]*>/i
};
};
@@ -24,6 +25,13 @@
CKEDITOR.htmlParser.prototype =
{
/**
+ * Function to be fired when a docType defintion is found. This function
+ * should be overriden when using this class.
+ * @param {String} docType The declaration text.
+ */
+ onDocType : function() {},
+
+ /**
* Function to be fired when a tag opener is found. This function
* should be overriden when using this class.
* @param {String} tagName The tag name. The name is guarantted to be
@@ -113,6 +121,14 @@
*/
parse : function( html )
{
+ // Remove docType declaration at first.
+ var self = this;
+ html = html.replace( this._.docTypeRegex, function( match )
+ {
+ self.onDocType( match );
+ return '';
+ } );
+
var parts,
tagName,
nextIndex = 0,