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