360 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
		
		
			
		
	
	
			360 lines
		
	
	
		
			9.9 KiB
		
	
	
	
		
			JavaScript
		
	
	
	
|  | /** | |||
|  |  * @license Copyright (c) 2003-2014, CKSource - Frederico Knabben. All rights reserved. | |||
|  |  * For licensing, see LICENSE.md or http://ckeditor.com/license
 | |||
|  |  */ | |||
|  | 
 | |||
|  | CKEDITOR.plugins.add( 'htmlwriter', { | |||
|  | 	init: function( editor ) { | |||
|  | 		var writer = new CKEDITOR.htmlWriter(); | |||
|  | 
 | |||
|  | 		writer.forceSimpleAmpersand = editor.config.forceSimpleAmpersand; | |||
|  | 		writer.indentationChars = editor.config.dataIndentationChars || '\t'; | |||
|  | 
 | |||
|  | 		// Overwrite default basicWriter initialized in hmtlDataProcessor constructor.
 | |||
|  | 		editor.dataProcessor.writer = writer; | |||
|  | 	} | |||
|  | } ); | |||
|  | 
 | |||
|  | /** | |||
|  |  * Class used to write HTML data. | |||
|  |  * | |||
|  |  *		var writer = new CKEDITOR.htmlWriter(); | |||
|  |  *		writer.openTag( 'p' ); | |||
|  |  *		writer.attribute( 'class', 'MyClass' ); | |||
|  |  *		writer.openTagClose( 'p' ); | |||
|  |  *		writer.text( 'Hello' ); | |||
|  |  *		writer.closeTag( 'p' ); | |||
|  |  *		alert( writer.getHtml() ); // '<p class="MyClass">Hello</p>'
 | |||
|  |  * | |||
|  |  * @class | |||
|  |  * @extends CKEDITOR.htmlParser.basicWriter | |||
|  |  */ | |||
|  | CKEDITOR.htmlWriter = CKEDITOR.tools.createClass( { | |||
|  | 	base: CKEDITOR.htmlParser.basicWriter, | |||
|  | 
 | |||
|  | 	/** | |||
|  | 	 * Creates a htmlWriter class instance. | |||
|  | 	 * | |||
|  | 	 * @constructor | |||
|  | 	 */ | |||
|  | 	$: function() { | |||
|  | 		// Call the base contructor.
 | |||
|  | 		this.base(); | |||
|  | 
 | |||
|  | 		/** | |||
|  | 		 * The characters to be used for each identation step. | |||
|  | 		 * | |||
|  | 		 *		// Use tab for indentation.
 | |||
|  | 		 *		editorInstance.dataProcessor.writer.indentationChars = '\t'; | |||
|  | 		 */ | |||
|  | 		this.indentationChars = '\t'; | |||
|  | 
 | |||
|  | 		/** | |||
|  | 		 * The characters to be used to close "self-closing" elements, like `<br>` or `<img>`. | |||
|  | 		 * | |||
|  | 		 *		// Use HTML4 notation for self-closing elements.
 | |||
|  | 		 *		editorInstance.dataProcessor.writer.selfClosingEnd = '>'; | |||
|  | 		 */ | |||
|  | 		this.selfClosingEnd = ' />'; | |||
|  | 
 | |||
|  | 		/** | |||
|  | 		 * The characters to be used for line breaks. | |||
|  | 		 * | |||
|  | 		 *		// Use CRLF for line breaks.
 | |||
|  | 		 *		editorInstance.dataProcessor.writer.lineBreakChars = '\r\n'; | |||
|  | 		 */ | |||
|  | 		this.lineBreakChars = '\n'; | |||
|  | 
 | |||
|  | 		this.sortAttributes = 1; | |||
|  | 
 | |||
|  | 		this._.indent = 0; | |||
|  | 		this._.indentation = ''; | |||
|  | 		// Indicate preformatted block context status. (#5789)
 | |||
|  | 		this._.inPre = 0; | |||
|  | 		this._.rules = {}; | |||
|  | 
 | |||
|  | 		var dtd = CKEDITOR.dtd; | |||
|  | 
 | |||
|  | 		for ( var e in CKEDITOR.tools.extend( {}, dtd.$nonBodyContent, dtd.$block, dtd.$listItem, dtd.$tableContent ) ) { | |||
|  | 			this.setRules( e, { | |||
|  | 				indent: !dtd[ e ][ '#' ], | |||
|  | 				breakBeforeOpen: 1, | |||
|  | 				breakBeforeClose: !dtd[ e ][ '#' ], | |||
|  | 				breakAfterClose: 1, | |||
|  | 				needsSpace: ( e in dtd.$block ) && !( e in { li: 1, dt: 1, dd: 1 } ) | |||
|  | 			} ); | |||
|  | 		} | |||
|  | 
 | |||
|  | 		this.setRules( 'br', { breakAfterOpen: 1 } ); | |||
|  | 
 | |||
|  | 		this.setRules( 'title', { | |||
|  | 			indent: 0, | |||
|  | 			breakAfterOpen: 0 | |||
|  | 		} ); | |||
|  | 
 | |||
|  | 		this.setRules( 'style', { | |||
|  | 			indent: 0, | |||
|  | 			breakBeforeClose: 1 | |||
|  | 		} ); | |||
|  | 
 | |||
|  | 		this.setRules( 'pre', { | |||
|  | 			breakAfterOpen: 1, // Keep line break after the opening tag
 | |||
|  | 			indent: 0 // Disable indentation on <pre>.
 | |||
|  | 		} ); | |||
|  | 	}, | |||
|  | 
 | |||
|  | 	proto: { | |||
|  | 		/** | |||
|  | 		 * Writes the tag opening part for a opener tag. | |||
|  | 		 * | |||
|  | 		 *		// Writes '<p'.
 | |||
|  | 		 *		writer.openTag( 'p', { class : 'MyClass', id : 'MyId' } ); | |||
|  | 		 * | |||
|  | 		 * @param {String} tagName The element name for this tag. | |||
|  | 		 * @param {Object} attributes The attributes defined for this tag. The | |||
|  | 		 * attributes could be used to inspect the tag. | |||
|  | 		 */ | |||
|  | 		openTag: function( tagName, attributes ) { | |||
|  | 			var rules = this._.rules[ tagName ]; | |||
|  | 
 | |||
|  | 			if ( this._.afterCloser && rules && rules.needsSpace && this._.needsSpace ) | |||
|  | 				this._.output.push( '\n' ); | |||
|  | 
 | |||
|  | 			if ( this._.indent ) | |||
|  | 				this.indentation(); | |||
|  | 			// Do not break if indenting.
 | |||
|  | 			else if ( rules && rules.breakBeforeOpen ) { | |||
|  | 				this.lineBreak(); | |||
|  | 				this.indentation(); | |||
|  | 			} | |||
|  | 
 | |||
|  | 			this._.output.push( '<', tagName ); | |||
|  | 
 | |||
|  | 			this._.afterCloser = 0; | |||
|  | 		}, | |||
|  | 
 | |||
|  | 		/** | |||
|  | 		 * Writes the tag closing part for a opener tag. | |||
|  | 		 * | |||
|  | 		 *		// Writes '>'.
 | |||
|  | 		 *		writer.openTagClose( 'p', false ); | |||
|  | 		 * | |||
|  | 		 *		// Writes ' />'.
 | |||
|  | 		 *		writer.openTagClose( 'br', true ); | |||
|  | 		 * | |||
|  | 		 * @param {String} tagName The element name for this tag. | |||
|  | 		 * @param {Boolean} isSelfClose Indicates that this is a self-closing tag, | |||
|  | 		 * like `<br>` or `<img>`. | |||
|  | 		 */ | |||
|  | 		openTagClose: function( tagName, isSelfClose ) { | |||
|  | 			var rules = this._.rules[ tagName ]; | |||
|  | 
 | |||
|  | 			if ( isSelfClose ) { | |||
|  | 				this._.output.push( this.selfClosingEnd ); | |||
|  | 
 | |||
|  | 				if ( rules && rules.breakAfterClose ) | |||
|  | 					this._.needsSpace = rules.needsSpace; | |||
|  | 			} else { | |||
|  | 				this._.output.push( '>' ); | |||
|  | 
 | |||
|  | 				if ( rules && rules.indent ) | |||
|  | 					this._.indentation += this.indentationChars; | |||
|  | 			} | |||
|  | 
 | |||
|  | 			if ( rules && rules.breakAfterOpen ) | |||
|  | 				this.lineBreak(); | |||
|  | 			tagName == 'pre' && ( this._.inPre = 1 ); | |||
|  | 		}, | |||
|  | 
 | |||
|  | 		/** | |||
|  | 		 * Writes an attribute. This function should be called after opening the | |||
|  | 		 * tag with {@link #openTagClose}. | |||
|  | 		 * | |||
|  | 		 *		// Writes ' class="MyClass"'.
 | |||
|  | 		 *		writer.attribute( 'class', 'MyClass' ); | |||
|  | 		 * | |||
|  | 		 * @param {String} attName The attribute name. | |||
|  | 		 * @param {String} attValue The attribute value. | |||
|  | 		 */ | |||
|  | 		attribute: function( attName, attValue ) { | |||
|  | 
 | |||
|  | 			if ( typeof attValue == 'string' ) { | |||
|  | 				this.forceSimpleAmpersand && ( attValue = attValue.replace( /&/g, '&' ) ); | |||
|  | 				// Browsers don't always escape special character in attribute values. (#4683, #4719).
 | |||
|  | 				attValue = CKEDITOR.tools.htmlEncodeAttr( attValue ); | |||
|  | 			} | |||
|  | 
 | |||
|  | 			this._.output.push( ' ', attName, '="', attValue, '"' ); | |||
|  | 		}, | |||
|  | 
 | |||
|  | 		/** | |||
|  | 		 * Writes a closer tag. | |||
|  | 		 * | |||
|  | 		 *		// Writes '</p>'.
 | |||
|  | 		 *		writer.closeTag( 'p' ); | |||
|  | 		 * | |||
|  | 		 * @param {String} tagName The element name for this tag. | |||
|  | 		 */ | |||
|  | 		closeTag: function( tagName ) { | |||
|  | 			var rules = this._.rules[ tagName ]; | |||
|  | 
 | |||
|  | 			if ( rules && rules.indent ) | |||
|  | 				this._.indentation = this._.indentation.substr( this.indentationChars.length ); | |||
|  | 
 | |||
|  | 			if ( this._.indent ) | |||
|  | 				this.indentation(); | |||
|  | 			// Do not break if indenting.
 | |||
|  | 			else if ( rules && rules.breakBeforeClose ) { | |||
|  | 				this.lineBreak(); | |||
|  | 				this.indentation(); | |||
|  | 			} | |||
|  | 
 | |||
|  | 			this._.output.push( '</', tagName, '>' ); | |||
|  | 			tagName == 'pre' && ( this._.inPre = 0 ); | |||
|  | 
 | |||
|  | 			if ( rules && rules.breakAfterClose ) { | |||
|  | 				this.lineBreak(); | |||
|  | 				this._.needsSpace = rules.needsSpace; | |||
|  | 			} | |||
|  | 
 | |||
|  | 			this._.afterCloser = 1; | |||
|  | 		}, | |||
|  | 
 | |||
|  | 		/** | |||
|  | 		 * Writes text. | |||
|  | 		 * | |||
|  | 		 *		// Writes 'Hello Word'.
 | |||
|  | 		 *		writer.text( 'Hello Word' ); | |||
|  | 		 * | |||
|  | 		 * @param {String} text The text value | |||
|  | 		 */ | |||
|  | 		text: function( text ) { | |||
|  | 			if ( this._.indent ) { | |||
|  | 				this.indentation(); | |||
|  | 				!this._.inPre && ( text = CKEDITOR.tools.ltrim( text ) ); | |||
|  | 			} | |||
|  | 
 | |||
|  | 			this._.output.push( text ); | |||
|  | 		}, | |||
|  | 
 | |||
|  | 		/** | |||
|  | 		 * Writes a comment. | |||
|  | 		 * | |||
|  | 		 *		// Writes "<!-- My comment -->".
 | |||
|  | 		 *		writer.comment( ' My comment ' ); | |||
|  | 		 * | |||
|  | 		 * @param {String} comment The comment text. | |||
|  | 		 */ | |||
|  | 		comment: function( comment ) { | |||
|  | 			if ( this._.indent ) | |||
|  | 				this.indentation(); | |||
|  | 
 | |||
|  | 			this._.output.push( '<!--', comment, '-->' ); | |||
|  | 		}, | |||
|  | 
 | |||
|  | 		/** | |||
|  | 		 * Writes a line break. It uses the {@link #lineBreakChars} property for it. | |||
|  | 		 * | |||
|  | 		 *		// Writes '\n' (e.g.).
 | |||
|  | 		 *		writer.lineBreak(); | |||
|  | 		 */ | |||
|  | 		lineBreak: function() { | |||
|  | 			if ( !this._.inPre && this._.output.length > 0 ) | |||
|  | 				this._.output.push( this.lineBreakChars ); | |||
|  | 			this._.indent = 1; | |||
|  | 		}, | |||
|  | 
 | |||
|  | 		/** | |||
|  | 		 * Writes the current indentation chars. It uses the {@link #indentationChars} | |||
|  | 		 * property, repeating it for the current indentation steps. | |||
|  | 		 * | |||
|  | 		 *		// Writes '\t' (e.g.).
 | |||
|  | 		 *		writer.indentation(); | |||
|  | 		 */ | |||
|  | 		indentation: function() { | |||
|  | 			if ( !this._.inPre && this._.indentation ) | |||
|  | 				this._.output.push( this._.indentation ); | |||
|  | 			this._.indent = 0; | |||
|  | 		}, | |||
|  | 
 | |||
|  | 		/** | |||
|  | 		 * Empties the current output buffer. It also brings back the default | |||
|  | 		 * values of the writer flags. | |||
|  | 		 * | |||
|  | 		 *		writer.reset(); | |||
|  | 		 */ | |||
|  | 		reset: function() { | |||
|  | 			this._.output = []; | |||
|  | 			this._.indent = 0; | |||
|  | 			this._.indentation = ''; | |||
|  | 			this._.afterCloser = 0; | |||
|  | 			this._.inPre = 0; | |||
|  | 		}, | |||
|  | 
 | |||
|  | 		/** | |||
|  | 		 * Sets formatting rules for a give element. The possible rules are: | |||
|  | 		 * | |||
|  | 		 * * `indent`: indent the element contents. | |||
|  | 		 * * `breakBeforeOpen`: break line before the opener tag for this element. | |||
|  | 		 * * `breakAfterOpen`: break line after the opener tag for this element. | |||
|  | 		 * * `breakBeforeClose`: break line before the closer tag for this element. | |||
|  | 		 * * `breakAfterClose`: break line after the closer tag for this element. | |||
|  | 		 * | |||
|  | 		 * All rules default to `false`. Each call to the function overrides | |||
|  | 		 * already present rules, leaving the undefined untouched. | |||
|  | 		 * | |||
|  | 		 * By default, all elements available in the {@link CKEDITOR.dtd#$block}, | |||
|  | 		 * {@link CKEDITOR.dtd#$listItem} and {@link CKEDITOR.dtd#$tableContent} | |||
|  | 		 * lists have all the above rules set to `true`. Additionaly, the `<br>` | |||
|  | 		 * element has the `breakAfterOpen` set to `true`. | |||
|  | 		 * | |||
|  | 		 *		// Break line before and after "img" tags.
 | |||
|  | 		 *		writer.setRules( 'img', { | |||
|  | 		 *			breakBeforeOpen: true | |||
|  | 		 *			breakAfterOpen: true | |||
|  | 		 *		} ); | |||
|  | 		 * | |||
|  | 		 *		// Reset the rules for the "h1" tag.
 | |||
|  | 		 *		writer.setRules( 'h1', {} ); | |||
|  | 		 * | |||
|  | 		 * @param {String} tagName The element name to which set the rules. | |||
|  | 		 * @param {Object} rules An object containing the element rules. | |||
|  | 		 */ | |||
|  | 		setRules: function( tagName, rules ) { | |||
|  | 			var currentRules = this._.rules[ tagName ]; | |||
|  | 
 | |||
|  | 			if ( currentRules ) | |||
|  | 				CKEDITOR.tools.extend( currentRules, rules, true ); | |||
|  | 			else | |||
|  | 				this._.rules[ tagName ] = rules; | |||
|  | 		} | |||
|  | 	} | |||
|  | } ); | |||
|  | 
 | |||
|  | /** | |||
|  |  * Whether to force using `'&'` instead of `'&'` in elements attributes | |||
|  |  * values, it's not recommended to change this setting for compliance with the | |||
|  |  * W3C XHTML 1.0 standards ([C.12, XHTML 1.0](http://www.w3.org/TR/xhtml1/#C_12)).
 | |||
|  |  * | |||
|  |  *		// Use `'&'` instead of `'&'`
 | |||
|  |  *		CKEDITOR.config.forceSimpleAmpersand = true; | |||
|  |  * | |||
|  |  * @cfg {Boolean} [forceSimpleAmpersand=false] | |||
|  |  * @member CKEDITOR.config | |||
|  |  */ | |||
|  | 
 | |||
|  | /** | |||
|  |  * The characters to be used for indenting the HTML produced by the editor. | |||
|  |  * Using characters different than `' '` (space) and `'\t'` (tab) is definitely | |||
|  |  * a bad idea as it'll mess the code. | |||
|  |  * | |||
|  |  *		// No indentation.
 | |||
|  |  *		CKEDITOR.config.dataIndentationChars = ''; | |||
|  |  * | |||
|  |  *		// Use two spaces for indentation.
 | |||
|  |  *		CKEDITOR.config.dataIndentationChars = '  '; | |||
|  |  * | |||
|  |  * @cfg {String} [dataIndentationChars='\t'] | |||
|  |  * @member CKEDITOR.config | |||
|  |  */ |