d06a12516adc94f91e55a79b1f2d9c1cad87a16b
[ckeditor.git] / _source / plugins / htmlwriter / plugin.js
1 /*
2 Copyright (c) 2003-2011, 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 "&lt;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 "&gt;".
142 * writer.openTagClose( 'p', false );
143 * @example
144 * // Writes " /&gt;".
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( /&amp;/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 "&lt;/p&gt;".
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 "&lt;!-- My comment --&gt;".
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 });