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