1 /*
  2  * CKEditor - The text editor for Internet - http://ckeditor.com
  3  * Copyright (C) 2003-2008 Frederico Caldeira Knabben
  4  *
  5  * == BEGIN LICENSE ==
  6  *
  7  * Licensed under the terms of any of the following licenses at your
  8  * choice:
  9  *
 10  *  - GNU General Public License Version 2 or later (the "GPL")
 11  *    http://www.gnu.org/licenses/gpl.html
 12  *
 13  *  - GNU Lesser General Public License Version 2.1 or later (the "LGPL")
 14  *    http://www.gnu.org/licenses/lgpl.html
 15  *
 16  *  - Mozilla Public License Version 1.1 or later (the "MPL")
 17  *    http://www.mozilla.org/MPL/MPL-1.1.html
 18  *
 19  * == END LICENSE ==
 20  */
 21
 22 /**
 23  * @fileOverview Defines the {@link CKEDITOR.editor} class, which represents an
 24  *		editor instance.
 25  */
 26
 27  /**
 28  * Represents an editor instance. This constructor should be rarely used, being
 29  * the standard replacement methods preferible.
 30  * @constructor
 31  * @param {CKEDITOR.dom.element} element The original element replaced by this
 32  *		editor instance.
 33  * @augments CKEDITOR.event
 34  * @example
 35  * var myTextarea = CKEDITOR.document.getById( 'myTextarea' );
 36  * var myEditor = <b>new CKEDITOR.editor( myTextarea )</b>;
 37  * CKEDITOR.add( myEditor );
 38  */
 39 CKEDITOR.editor = ( function()
 40 {
 41 	// The counter for automatic instance names.
 42 	var nameCounter = 0;
 43
 44 	var getNewName = function()
 45 	{
 46 		var name = 'editor' + ( ++nameCounter );
 47 		return CKEDITOR.instances[ name ] ? getNewName() : name;
 48 	};
 49
 50 	// ##### START: Config Privates
 51
 52 	// These function loads custom configuration files and cache the
 53 	// CKEDITOR.editorConfig functions defined on them, so there is no need to
 54 	// download them more than once for several instances.
 55 	var loadConfigLoaded = {};
 56 	var loadConfig = function( editor )
 57 	{
 58 		var customConfig = editor.config.customConfig;
 59
 60 		// Check if there is a custom config to load.
 61 		if ( !customConfig )
 62 			return false;
 63
 64 		var loadedConfig = loadConfigLoaded[ customConfig ] || ( loadConfigLoaded[ customConfig ] = { editors : [] } );
 65
 66 		// If the custom config has already been downloaded, reuse it.
 67 		if ( loadedConfig.fn )
 68 		{
 69 			// Call the cached CKEDITOR.editorConfig defined in the custom
 70 			// config file for the editor instance depending on it.
 71 			loadedConfig.fn.call( editor, editor );
 72
 73 			// If there is no other customConfig in the chain, fire the
 74 			// "configLoaded" event.
 75 			if ( editor.config.customConfig == customConfig || !loadConfig( editor ) )
 76 				editor.fireOnce( 'customConfigLoaded' );
 77 		}
 78 		else
 79 		{
 80 			// Add the editor to the list of editors waiting for this config.
 81 			loadedConfig.editors.push( editor );
 82
 83 			// Load the custom configuration file.
 84 			CKEDITOR.scriptLoader.load( customConfig, function()
 85 				{
 86 					// If the CKEDITOR.editorConfig function has been properly
 87 					// defined in the custom configuration file, cache it.
 88 					if ( CKEDITOR.editorConfig )
 89 						loadedConfig.fn = CKEDITOR.editorConfig;
 90 					else
 91 						loadedConfig.fn = function(){};
 92
 93 					delete CKEDITOR.editorConfig;
 94
 95 					for ( var i = 0, length = loadedConfig.editors.length ; i < length ; i++ )
 96 					{
 97 						// Call the load config again. This time the custom
 98 						// config is already cached and so it will get loaded.
 99 						loadConfig( loadedConfig.editors[ i ] );
100 					}
101
102 					delete loadedConfig.editors;
103 				});
104 		}
105
106 		return true;
107 	};
108
109 	var initConfig = function( editor, instanceConfig )
110 	{
111 		// Setup the lister for the "customConfigLoaded" event.
112 		editor.on( 'customConfigLoaded', function()
113 			{
114 				// Overwrite the settings from the in-page config.
115 				if ( instanceConfig )
116 					CKEDITOR.tools.extend( editor.config, instanceConfig, true );
117
118 				// Fire the "configLoaded" event.
119 				editor.fireOnce( 'configLoaded' );
120
121 				// Start loading the plugins.
122 				loadPlugins( editor );
123 			});
124
125 		// The instance config may override the customConfig setting to avoid
126 		// loading the default ~/config.js file.
127 		if ( instanceConfig && instanceConfig.customConfig != undefined )
128 			editor.config.customConfig = instanceConfig.customConfig;
129
130 		// Load configs from the custom configuration files.
131 		if ( !loadConfig( editor ) )
132 			editor.fireOnce( 'customConfigLoaded' );
133 	};
134
135 	// Basic config class to inherit the default settings from CKEDITOR.config.
136 	var config = function()
137 	{};
138 	config.prototype = CKEDITOR.config;
139
140 	// ##### END: Config Privates
141
142 	var loadPlugins = function( editor )
143 	{
144 		// Load all plugins defined in the "plugins" setting.
145 		CKEDITOR.plugins.load( editor.config.plugins.split( ',' ), function( plugins )
146 			{
147 				// Cache the loaded plugin names.
148 				editor.plugins = plugins;
149
150 				// Initialize all plugins that have the "beforeInit" and "init" methods defined.
151 				var methods = [ 'beforeInit', 'init' ];
152 				for ( var m = 0 ; m < methods.length ; m++ )
153 				{
154 					for ( var i = 0 ; i < plugins.length ; i++ )
155 					{
156 						var pluginName = plugins[ i ];
157 						var plugin = CKEDITOR.plugins.get( pluginName );
158 						if ( plugin && plugin[ methods[ m ] ] )
159 							plugin[ methods[ m ] ]( editor, CKEDITOR.plugins.getPath( pluginName ) );
160 					}
161 				}
162
163 				// Load the editor skin and theme.
164 				loadSkinTheme( editor );
165 			});
166 	};
167
168 	var loadSkinTheme = function( editor )
169 	{
170 		// Load the skin.
171 		CKEDITOR.skins.load( editor.config.skin, 'editor' );
172
173 		// Load the theme.
174 		var theme = editor.config.theme;
175 		CKEDITOR.themes.load( theme, function()
176 			{
177 				CKEDITOR.themes.get( theme ).build( editor, CKEDITOR.themes.getPath( theme ) );
178 			});
179 	};
180
181 	return function( element, instanceConfig )
182 	{
183 		this._ =
184 		{
185 			commands : {}
186 		};
187
188 		// Call the base constructor.
189 		CKEDITOR.event.call( this );
190
191 		/**
192 		 * The DOM element that has been replaced by this editor instance. This
193 		 * element holds the editor data on load and post.
194 		 * @name CKEDITOR.editor.prototype.element
195 		 * @type CKEDITOR.dom.element
196 		 * @example
197 		 * var editor = CKEDITOR.instances.editor1;
198 		 * alert( <b>editor.element</b>.getName() );  "textarea"
199 		 */
200 		this.element = element;
201
202 		/**
203 		 * The editor instance name. It hay be the replaced element id, name or
204 		 * a default name using a progressive counter (editor1, editor2, ...).
205 		 * @name CKEDITOR.editor.prototype.name
206 		 * @type String
207 		 * @example
208 		 * var editor = CKEDITOR.instances.editor1;
209 		 * alert( <b>editor.name</b> );  "editor1"
210 		 */
211 		this.name = element.getId() || element.getNameAtt() || getNewName();
212
213 		/**
214 		 * The configurations for this editor instance. It inherits all
215 		 * settings defined in (@link CKEDITOR.config}, combined with settings
216 		 * loaded from custom configuration files and those defined inline in
217 		 * the page when creating the editor.
218 		 * @name CKEDITOR.editor.prototype.config
219 		 * @type Object
220 		 * @example
221 		 * var editor = CKEDITOR.instances.editor1;
222 		 * alert( <b>editor.config.theme</b> );  "default" e.g.
223 		 */
224 		this.config = new config();
225
226 		/**
227 		 * Namespace containing UI features related to this editor instance.
228 		 * @name CKEDITOR.editor.prototype.ui
229 		 * @type CKEDITOR.ui
230 		 * @example
231 		 */
232 		this.ui = new CKEDITOR.ui( this );
233
234 		// Call initConfig using events, to be sure that instanceCreated is
235 		// fired first.
236 		this.on( 'instanceCreated', function()
237 			{
238 				initConfig( this, instanceConfig );
239 			});
240 	};
241 })();
242
243 CKEDITOR.editor.prototype =
244 {
245 	/**
246 	 * Adds a command definition to the editor instance. Commands added with
247 	 * this function can be later executed with {@link #execCommand}.
248 	 * @param {String} commandName The indentifier name of the command.
249 	 * @param {CKEDITOR.commandDefinition} commandDefinition The command definition.
250 	 * @example
251 	 * editorInstance.addCommand( 'sample',
252 	 * {
253 	 *     exec : function( editor )
254 	 *     {
255 	 *         alert( 'Executing a command for the editor name "' + editor.name + '"!' );
256 	 *     }
257 	 * });
258 	 */
259 	addCommand : function( commandName, commandDefinition )
260 	{
261 		this._.commands[ commandName ] = commandDefinition;
262 	},
263
264 	/**
265 	 * Executes a command.
266 	 * @param {String} commandName The indentifier name of the command.
267 	 * @param {Object} [data] Data to be passed to the command
268 	 * @example
269 	 * editorInstance.execCommand( 'Bold' );
270 	 */
271 	execCommand : function( commandName, data )
272 	{
273 		var command = this._.commands[ commandName ];
274 		if ( command )
275 			return command.exec( this, data );
276
277 		throw 'Unknown command name "' + commandName + '"';
278 	},
279
280 	// Both fire and fireOnce will always pass this editor instance as the
281 	// "editor" param in CKEDITOR.event.fire. So, we override it to do that
282 	// automaticaly.
283
284 	/** @ignore */
285 	fire : function( eventName, data )
286 	{
287 		return CKEDITOR.event.prototype.fire.call( this, eventName, data, this );
288 	},
289
290 	/** @ignore */
291 	fireOnce : function( eventName, data )
292 	{
293 		return CKEDITOR.event.prototype.fireOnce.call( this, eventName, data, this );
294 	},
295
296 	/**
297 	 * Gets the editor data. The data will be in raw format. It is the same
298 	 * data that is posted by the editor.
299 	 * @type String
300 	 * @returns (String) The editor data.
301 	 * @example
302 	 * if ( CKEDITOR.instances.editor1.<b>getData()</b> == '' )
303 	 *     alert( 'There is no data available' );
304 	 */
305 	getData : function()
306 	{
307 		this.fire( 'beforeGetData' );
308
309 		// Fire "getData" so data manipulation may happen.
310 		var eventData = { dataValue : this._.data || this.element.$.value };
311 		this.fire( 'getData', eventData );
312
313 		return eventData.dataValue;
314 	},
315
316 	/**
317 	 * Sets the editor data. The data must be provided in raw format.
318 	 * @example
319 	 * CKEDITOR.instances.editor1.<b>setData( '<p>This is the editor data.</p>' )</b>;
320 	 */
321 	setData : function( data )
322 	{
323 		// Fire "setData" so data manipulation may happen.
324 		var eventData = { dataValue : data };
325 		this.fire( 'setData', eventData );
326
327 		this._.data = eventData.dataValue;
328
329 		this.fire( 'afterSetData' );
330 	},
331
332 	/**
333 	 * Updates the <textarea> element that has been replaced by the editor with
334 	 * the current data available in the editor.
335 	 * @example
336 	 * CKEDITOR.instances.editor1.updateElement();
337 	 * alert( document.getElementById( 'editor1' ).value );  // The current editor data.
338 	 */
339 	updateElement : function()
340 	{
341 		this.element.$.value = this.getData();
342 	}
343 };
344
345 // "Inherit" (copy actually) from CKEDITOR.event.
346 CKEDITOR.event.implementOn( CKEDITOR.editor.prototype );
347