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 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 = false;
 61
 62 		this.sortAttributes = true;
 63
 64 		this._.indent = false;
 65 		this._.indentation = '';
 66 		this._.rules = {};
 67
 68 		var dtd = CKEDITOR.dtd;
 69
 70 		for ( var e in CKEDITOR.tools.extend( {}, dtd.$block, dtd.$listItem, dtd.$tableContent ) )
 71 		{
 72 			this.setRules( e,
 73 				{
 74 					indent : true,
 75 					breakBeforeOpen : true,
 76 					breakAfterOpen : true,
 77 					breakBeforeClose : !dtd[ e ][ '#' ],
 78 					breakAfterClose : true
 79 				});
 80 		}
 81
 82 		this.setRules( 'br',
 83 			{
 84 				breakAfterOpen : true
 85 			});
 86 	},
 87
 88 	proto :
 89 	{
 90 		/**
 91 		 * Writes the tag opening part for a opener tag.
 92 		 * @param {String} tagName The element name for this tag.
 93 		 * @param {Object} attributes The attributes defined for this tag. The
 94 		 *		attributes could be used to inspect the tag.
 95 		 * @example
 96 		 * // Writes "<p".
 97 		 * writer.openTag( 'p', { class : 'MyClass', id : 'MyId' } );
 98 		 */
 99 		openTag : function( tagName, attributes )
100 		{
101 			var rules = this._.rules[ tagName ];
102
103 			if ( this._.indent )
104 				this.indentation();
105 			// Do not break if indenting.
106 			else if ( rules && rules.breakBeforeOpen )
107 			{
108 				this.lineBreak();
109 				this.indentation();
110 			}
111
112 			this._.output.push( '<', tagName );
113 		},
114
115 		/**
116 		 * Writes the tag closing part for a opener tag.
117 		 * @param {String} tagName The element name for this tag.
118 		 * @param {Boolean} isSelfClose Indicates that this is a self-closing tag,
119 		 *		like "br" or "img".
120 		 * @example
121 		 * // Writes ">".
122 		 * writer.openTagClose( 'p', false );
123 		 * @example
124 		 * // Writes " />".
125 		 * writer.openTagClose( 'br', true );
126 		 */
127 		openTagClose : function( tagName, isSelfClose )
128 		{
129 			var rules = this._.rules[ tagName ];
130
131 			if ( isSelfClose )
132 				this._.output.push( this.selfClosingEnd );
133 			else
134 			{
135 				this._.output.push( '>' );
136
137 				if ( rules && rules.indent )
138 					this._.indentation += this.indentationChars;
139 			}
140
141 			if ( rules && rules.breakAfterOpen )
142 				this.lineBreak();
143 		},
144
145 		/**
146 		 * Writes an attribute. This function should be called after opening the
147 		 * tag with {@link #openTagClose}.
148 		 * @param {String} attName The attribute name.
149 		 * @param {String} attValue The attribute value.
150 		 * @example
151 		 * // Writes ' class="MyClass"'.
152 		 * writer.attribute( 'class', 'MyClass' );
153 		 */
154 		attribute : function( attName, attValue )
155 		{
156 			if ( this.forceSimpleAmpersand )
157 				attValue = attValue.replace( /&/, '&' );
158
159 			this._.output.push( ' ', attName, '="', attValue, '"' );
160 		},
161
162 		/**
163 		 * Writes a closer tag.
164 		 * @param {String} tagName The element name for this tag.
165 		 * @example
166 		 * // Writes "</p>".
167 		 * writer.closeTag( 'p' );
168 		 */
169 		closeTag : function( tagName )
170 		{
171 			var rules = this._.rules[ tagName ];
172
173 			if ( rules && rules.indent )
174 				this._.indentation = this._.indentation.substr( this.indentationChars.length );
175
176 			if ( this._.indent )
177 				this.indentation();
178 			// Do not break if indenting.
179 			else if ( rules && rules.breakBeforeClose )
180 			{
181 				this.lineBreak();
182 				this.indentation();
183 			}
184
185 			this._.output.push( '</', tagName, '>' );
186
187 			if ( rules && rules.breakAfterClose )
188 				this.lineBreak();
189 		},
190
191 		/**
192 		 * Writes text.
193 		 * @param {String} text The text value
194 		 * @example
195 		 * // Writes "Hello Word".
196 		 * writer.text( 'Hello Word' );
197 		 */
198 		text : function( text )
199 		{
200 			if ( this._.indent )
201 			{
202 				this.indentation();
203 				text = CKEDITOR.tools.ltrim( text );
204 			}
205
206 			this._.output.push( text );
207 		},
208
209 		/**
210 		 * Writes a comment.
211 		 * @param {String} comment The comment text.
212 		 * @example
213 		 * // Writes "<!-- My comment -->".
214 		 * writer.comment( ' My comment ' );
215 		 */
216 		comment : function( comment )
217 		{
218 			if ( this._.indent )
219 				this.indentation();
220
221 			this._.output.push( '<!--', comment, '-->' );
222 		},
223
224 		/**
225 		 * Writes a line break. It uses the {@link #lineBreakChars} property for it.
226 		 * @example
227 		 * // Writes "\n" (e.g.).
228 		 * writer.lineBreak();
229 		 */
230 		lineBreak : function()
231 		{
232 			if ( this._.output.length > 0 )
233 				this._.output.push( this.lineBreakChars );
234 			this._.indent = true;
235 		},
236
237 		/**
238 		 * Writes the current indentation chars. It uses the
239 		 * {@link #indentationChars} property, repeating it for the current
240 		 * indentation steps.
241 		 * @example
242 		 * // Writes "\t" (e.g.).
243 		 * writer.indentation();
244 		 */
245 		indentation : function()
246 		{
247 			this._.output.push( this._.indentation );
248 			this._.indent = false;
249 		},
250
251 		/**
252 		 * Sets formatting rules for a give element. The possible rules are:
253 		 * <ul>
254 		 *	<li><b>indent</b>: indent the element contents.</li>
255 		 *	<li><b>breakBeforeOpen</b>: break line before the opener tag for this element.</li>
256 		 *	<li><b>breakAfterOpen</b>: break line after the opener tag for this element.</li>
257 		 *	<li><b>breakBeforeClose</b>: break line before the closer tag for this element.</li>
258 		 *	<li><b>breakAfterClose</b>: break line after the closer tag for this element.</li>
259 		 * </ul>
260 		 *
261 		 * All rules default to "false".
262 		 *
263 		 * By default, all elements available in the {@link CKEDITOR.dtd.$block),
264 		 * {@link CKEDITOR.dtd.$listItem} and {@link CKEDITOR.dtd.$tableContent}
265 		 * lists have all the above rules set to "true". Additionaly, the "br"
266 		 * element has the "breakAfterOpen" set to "true".
267 		 * @param {String} tagName The element name to which set the rules.
268 		 * @param {Object} rules An object containing the element rules.
269 		 * @example
270 		 * // Break line before and after "img" tags.
271 		 * writer.setRules( 'img',
272 		 *     {
273 		 *         breakBeforeOpen : true
274 		 *         breakAfterOpen : true
275 		 *     });
276 		 * @example
277 		 * // Reset the rules for the "h1" tag.
278 		 * writer.setRules( 'h1', {} );
279 		 */
280 		setRules : function( tagName, rules )
281 		{
282 			this._.rules[ tagName ] = rules;
283 		}
284 	}
285 });
286