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