1 /* 2 Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 CKEDITOR.plugins.add( 'htmlwriter' ); 7 8 /** 9 * Class used to write HTML data. 10 * @constructor 11 * @example 12 * var writer = new CKEDITOR.htmlWriter(); 13 * writer.openTag( 'p' ); 14 * writer.attribute( 'class', 'MyClass' ); 15 * writer.openTagClose( 'p' ); 16 * writer.text( 'Hello' ); 17 * writer.closeTag( 'p' ); 18 * alert( writer.getHtml() ); "<p class="MyClass">Hello</p>" 19 */ 20 CKEDITOR.htmlWriter = CKEDITOR.tools.createClass( 21 { 22 base : CKEDITOR.htmlParser.basicWriter, 23 24 $ : function() 25 { 26 // Call the base contructor. 27 this.base(); 28 29 /** 30 * The characters to be used for each identation step. 31 * @type String 32 * @default "\t" (tab) 33 * @example 34 * // Use two spaces for indentation. 35 * editorInstance.dataProcessor.writer.indentationChars = ' '; 36 */ 37 this.indentationChars = '\t'; 38 39 /** 40 * The characters to be used to close "self-closing" elements, like "br" or 41 * "img". 42 * @type String 43 * @default " />" 44 * @example 45 * // Use HTML4 notation for self-closing elements. 46 * editorInstance.dataProcessor.writer.selfClosingEnd = '>'; 47 */ 48 this.selfClosingEnd = ' />'; 49 50 /** 51 * The characters to be used for line breaks. 52 * @type String 53 * @default "\n" (LF) 54 * @example 55 * // Use CRLF for line breaks. 56 * editorInstance.dataProcessor.writer.lineBreakChars = '\r\n'; 57 */ 58 this.lineBreakChars = '\n'; 59 60 this.forceSimpleAmpersand = 0; 61 62 this.sortAttributes = 1; 63 64 this._.indent = 0; 65 this._.indentation = ''; 66 // Indicate preformatted block context status. (#5789) 67 this._.inPre = 0; 68 this._.rules = {}; 69 70 var dtd = CKEDITOR.dtd; 71 72 for ( var e in CKEDITOR.tools.extend( {}, dtd.$nonBodyContent, dtd.$block, dtd.$listItem, dtd.$tableContent ) ) 73 { 74 this.setRules( e, 75 { 76 indent : 1, 77 breakBeforeOpen : 1, 78 breakAfterOpen : 1, 79 breakBeforeClose : !dtd[ e ][ '#' ], 80 breakAfterClose : 1 81 }); 82 } 83 84 this.setRules( 'br', 85 { 86 breakAfterOpen : 1 87 }); 88 89 this.setRules( 'title', 90 { 91 indent : 0, 92 breakAfterOpen : 0 93 }); 94 95 this.setRules( 'style', 96 { 97 indent : 0, 98 breakBeforeClose : 1 99 }); 100 101 // Disable indentation on <pre>. 102 this.setRules( 'pre', 103 { 104 indent : 0 105 }); 106 }, 107 108 proto : 109 { 110 /** 111 * Writes the tag opening part for a opener tag. 112 * @param {String} tagName The element name for this tag. 113 * @param {Object} attributes The attributes defined for this tag. The 114 * attributes could be used to inspect the tag. 115 * @example 116 * // Writes "<p". 117 * writer.openTag( 'p', { class : 'MyClass', id : 'MyId' } ); 118 */ 119 openTag : function( tagName, attributes ) 120 { 121 var rules = this._.rules[ tagName ]; 122 123 if ( this._.indent ) 124 this.indentation(); 125 // Do not break if indenting. 126 else if ( rules && rules.breakBeforeOpen ) 127 { 128 this.lineBreak(); 129 this.indentation(); 130 } 131 132 this._.output.push( '<', tagName ); 133 }, 134 135 /** 136 * Writes the tag closing part for a opener tag. 137 * @param {String} tagName The element name for this tag. 138 * @param {Boolean} isSelfClose Indicates that this is a self-closing tag, 139 * like "br" or "img". 140 * @example 141 * // Writes ">". 142 * writer.openTagClose( 'p', false ); 143 * @example 144 * // Writes " />". 145 * writer.openTagClose( 'br', true ); 146 */ 147 openTagClose : function( tagName, isSelfClose ) 148 { 149 var rules = this._.rules[ tagName ]; 150 151 if ( isSelfClose ) 152 this._.output.push( this.selfClosingEnd ); 153 else 154 { 155 this._.output.push( '>' ); 156 157 if ( rules && rules.indent ) 158 this._.indentation += this.indentationChars; 159 } 160 161 if ( rules && rules.breakAfterOpen ) 162 this.lineBreak(); 163 tagName == 'pre' && ( this._.inPre = 1 ); 164 }, 165 166 /** 167 * Writes an attribute. This function should be called after opening the 168 * tag with {@link #openTagClose}. 169 * @param {String} attName The attribute name. 170 * @param {String} attValue The attribute value. 171 * @example 172 * // Writes ' class="MyClass"'. 173 * writer.attribute( 'class', 'MyClass' ); 174 */ 175 attribute : function( attName, attValue ) 176 { 177 178 if ( typeof attValue == 'string' ) 179 { 180 this.forceSimpleAmpersand && ( attValue = attValue.replace( /&/g, '&' ) ); 181 // Browsers don't always escape special character in attribute values. (#4683, #4719). 182 attValue = CKEDITOR.tools.htmlEncodeAttr( attValue ); 183 } 184 185 this._.output.push( ' ', attName, '="', attValue, '"' ); 186 }, 187 188 /** 189 * Writes a closer tag. 190 * @param {String} tagName The element name for this tag. 191 * @example 192 * // Writes "</p>". 193 * writer.closeTag( 'p' ); 194 */ 195 closeTag : function( tagName ) 196 { 197 var rules = this._.rules[ tagName ]; 198 199 if ( rules && rules.indent ) 200 this._.indentation = this._.indentation.substr( this.indentationChars.length ); 201 202 if ( this._.indent ) 203 this.indentation(); 204 // Do not break if indenting. 205 else if ( rules && rules.breakBeforeClose ) 206 { 207 this.lineBreak(); 208 this.indentation(); 209 } 210 211 this._.output.push( '</', tagName, '>' ); 212 tagName == 'pre' && ( this._.inPre = 0 ); 213 214 if ( rules && rules.breakAfterClose ) 215 this.lineBreak(); 216 }, 217 218 /** 219 * Writes text. 220 * @param {String} text The text value 221 * @example 222 * // Writes "Hello Word". 223 * writer.text( 'Hello Word' ); 224 */ 225 text : function( text ) 226 { 227 if ( this._.indent ) 228 { 229 this.indentation(); 230 !this._.inPre && ( text = CKEDITOR.tools.ltrim( text ) ); 231 } 232 233 this._.output.push( text ); 234 }, 235 236 /** 237 * Writes a comment. 238 * @param {String} comment The comment text. 239 * @example 240 * // Writes "<!-- My comment -->". 241 * writer.comment( ' My comment ' ); 242 */ 243 comment : function( comment ) 244 { 245 if ( this._.indent ) 246 this.indentation(); 247 248 this._.output.push( '<!--', comment, '-->' ); 249 }, 250 251 /** 252 * Writes a line break. It uses the {@link #lineBreakChars} property for it. 253 * @example 254 * // Writes "\n" (e.g.). 255 * writer.lineBreak(); 256 */ 257 lineBreak : function() 258 { 259 if ( !this._.inPre && this._.output.length > 0 ) 260 this._.output.push( this.lineBreakChars ); 261 this._.indent = 1; 262 }, 263 264 /** 265 * Writes the current indentation chars. It uses the 266 * {@link #indentationChars} property, repeating it for the current 267 * indentation steps. 268 * @example 269 * // Writes "\t" (e.g.). 270 * writer.indentation(); 271 */ 272 indentation : function() 273 { 274 if( !this._.inPre ) 275 this._.output.push( this._.indentation ); 276 this._.indent = 0; 277 }, 278 279 /** 280 * Sets formatting rules for a give element. The possible rules are: 281 * <ul> 282 * <li><b>indent</b>: indent the element contents.</li> 283 * <li><b>breakBeforeOpen</b>: break line before the opener tag for this element.</li> 284 * <li><b>breakAfterOpen</b>: break line after the opener tag for this element.</li> 285 * <li><b>breakBeforeClose</b>: break line before the closer tag for this element.</li> 286 * <li><b>breakAfterClose</b>: break line after the closer tag for this element.</li> 287 * </ul> 288 * 289 * All rules default to "false". Each call to the function overrides 290 * already present rules, leaving the undefined untouched. 291 * 292 * By default, all elements available in the {@link CKEDITOR.dtd.$block), 293 * {@link CKEDITOR.dtd.$listItem} and {@link CKEDITOR.dtd.$tableContent} 294 * lists have all the above rules set to "true". Additionaly, the "br" 295 * element has the "breakAfterOpen" set to "true". 296 * @param {String} tagName The element name to which set the rules. 297 * @param {Object} rules An object containing the element rules. 298 * @example 299 * // Break line before and after "img" tags. 300 * writer.setRules( 'img', 301 * { 302 * breakBeforeOpen : true 303 * breakAfterOpen : true 304 * }); 305 * @example 306 * // Reset the rules for the "h1" tag. 307 * writer.setRules( 'h1', {} ); 308 */ 309 setRules : function( tagName, rules ) 310 { 311 var currentRules = this._.rules[ tagName ]; 312 313 if ( currentRules ) 314 CKEDITOR.tools.extend( currentRules, rules, true ); 315 else 316 this._.rules[ tagName ] = rules; 317 } 318 } 319 }); 320