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 The "wysiwygarea" plugin. It registers the "wysiwyg" editing
 24  *		mode, which handles the main editing area space.
 25  */
 26
 27 (function()
 28 {
 29 	// Matches all self-closing tags that are not defined as empty elements in
 30 	// the DTD (like <span/>).
 31 	var invalidSelfCloseTagsRegex = /(<(?!br|hr|base|meta|link|param|img|area|input|col)([a-zA-Z0-9:]+)[^>]*)\/>/gi;
 32
 33 	// #### protectEvents - START
 34
 35 	// Matches all tags that have event attributes (onXYZ).
 36 	var tagsWithEventRegex = /<[^\>]+ on\w+\s*=[\s\S]+?\>/g;
 37
 38 	// Matches all event attributes.
 39 	var eventAttributesRegex = /\s(on\w+)(?=\s*=\s*?('|")[\s\S]*?\2)/g;
 40
 41 	// Matches the protected attribute prefix.
 42 	var protectedEventsRegex = /_cke_pa_/g;
 43
 44 	var protectEvents = function( html )
 45 	{
 46 		return html.replace( tagsWithEventRegex, protectEvents_ReplaceTags );
 47 	};
 48
 49 	var protectEvents_ReplaceTags = function( tagMatch )
 50 	{
 51 		// Appends the "_cke_pa_" prefix to the event name.
 52 		return tagMatch.replace( eventAttributesRegex, ' _cke_pa_$1' );
 53 	};
 54
 55 	var protectEventsRestore = function( html )
 56 	{
 57 		return html.replace( protectedEventsRegex, '' ) ;
 58 	};
 59
 60 	// #### protectEvents - END
 61
 62 	CKEDITOR.plugins.add( 'wysiwygarea',
 63 	{
 64 		init : function( editor, pluginPath )
 65 		{
 66 			editor.on( 'editingBlockReady', function()
 67 				{
 68 					var mainElement,
 69 						iframe,
 70 						isLoadingData,
 71 						isPendingFocus;
 72
 73 					// The following information is needed for IE only.
 74 					var isCustomDomain = CKEDITOR.env.ie && document.domain != window.location.hostname;
 75
 76 					// Creates the iframe that holds the editable document.
 77 					var createIFrame = function()
 78 					{
 79 						if ( iframe )
 80 							iframe.remove();
 81
 82 						iframe = new CKEDITOR.dom.element( 'iframe' )
 83 							.setAttributes({
 84 								frameBorder : 0,
 85 								allowTransparency : true })
 86 							.setStyles({
 87 								width : '100%',
 88 								height : '100%' });
 89
 90 						if ( CKEDITOR.env.ie )
 91 						{
 92 							if ( isCustomDomain )
 93 							{
 94 								// The document domain must be set within the src
 95 								// attribute.
 96 								iframe.setAttribute( 'src',
 97 									'javascript:void( (function(){' +
 98 										'document.open();' +
 99 										'document.domain="' + document.domain + '";' +
100 										'document.write( window.parent._cke_htmlToLoad_' + editor.name + ' );' +
101 										'document.close();' +
102 										'window.parent._cke_htmlToLoad_' + editor.name + ' = null;' +
103 									'})() )' );
104 							}
105 							else
106 								// To avoid HTTPS warnings.
107 								iframe.setAttribute( 'src', 'javascript:void(0)' );
108 						}
109
110 						// Append the new IFRAME to the main element. For IE, it
111 						// must be done after setting the "src", to avoid the
112 						// "secure/unsecure" message under HTTPS.
113 						mainElement.append( iframe );
114 					};
115
116 					// The script that is appended to the data being loaded. It
117 					// enables editing, and makes some
118 					var activationScript =
119 						'<script id="cke_actscrpt" type="text/javascript">' +
120 							'window.onload = function()' +
121 							'{' +
122 								// Remove this script from the DOM.
123 								'var s = document.getElementById( "cke_actscrpt" );' +
124 								's.parentNode.removeChild( s );' +
125
126 								// Call the temporary function for the editing
127 								// boostrap.
128 								'window.parent.CKEDITOR.instances.' + editor.name + '._.contentDomReady( window );' +
129 							'}' +
130 						'</script>';
131
132 					// Editing area bootstrap code.
133 					var contentDomReady = function( domWindow )
134 					{
135 						delete editor._.contentDomReady;
136
137 						var domDocument = domWindow.document,
138 							body = domDocument.body;
139
140 						body.spellcheck = !editor.config.disableNativeSpellChecker;
141
142 						if ( CKEDITOR.env.ie )
143 						{
144 							// Disable and re-enable the body to avoid IE from
145 							// taking the editing focus at startup. (#141 / #523)
146 							body.disabled = true;
147 							body.contentEditable = true;
148 							body.removeAttribute( 'disabled' );
149 						}
150 						else
151 							domDocument.designMode = 'on';
152
153 						// IE, Opera and Safari may not support it and throw
154 						// errors.
155 						try { domDocument.execCommand( 'enableObjectResizing', false, !editor.config.disableObjectResizing ) ; } catch(e) {}
156 						try { domDocument.execCommand( 'enableInlineTableEditing', false, !editor.config.disableNativeTableHandles ) ; } catch(e) {}
157
158 						editor.window	= new CKEDITOR.dom.window( domWindow );
159 						editor.document	= new CKEDITOR.dom.document( domDocument );
160
161 						editor.fire( 'contentDom' );
162
163 						isLoadingData = false;
164
165 						if ( isPendingFocus )
166 							editor.focus();
167 					};
168
169 					editor.addMode( 'wysiwyg',
170 						{
171 							load : function( holderElement, data, isSnapshot )
172 							{
173 								mainElement = holderElement;
174
175 								// Create the iframe at load for all browsers
176 								// except FF and IE with custom domain.
177 								if ( !isCustomDomain || !CKEDITOR.env.gecko )
178 									createIFrame();
179
180 								if ( isSnapshot )
181 									this.loadSnapshotData( data );
182 								else
183 									this.loadData( data );
184 							},
185
186 							loadData : function( data )
187 							{
188 								isLoadingData = true;
189
190 								// Fix for invalid self-closing tags (see #152).
191 								// TODO: Check if this fix is really needed as
192 								// soon as we have the XHTML generator.
193 								if ( CKEDITOR.env.ie )
194 									data = data.replace( invalidSelfCloseTagsRegex, '$1></$2>' );
195
196 								// Prevent event attributes (like "onclick") to
197 								// execute while editing.
198 								if ( CKEDITOR.env.ie || CKEDITOR.env.webkit )
199 									data = protectEvents( data );
200
201 								data =
202 									CKEDITOR.config.docType +
203 									'<html dir="' + CKEDITOR.config.contentLangDirection + '">' +
204 									'<head>' +
205 										'<link href="' + CKEDITOR.config.contentsCss + '" type="text/css" rel="stylesheet" _fcktemp="true"/>' +
206 									'</head>' +
207 									'<body>' +
208 										editor.dataProcessor.toHtml( data ) +
209 									'</body>' +
210 									'</html>' +
211 									activationScript;
212
213 								// For custom domain in IE, set the global variable
214 								// that will temporarily hold the editor data. This
215 								// reference will be used in the ifram src.
216 								if ( isCustomDomain )
217 									window[ '_cke_htmlToLoad_' + editor.name ] = data;
218
219 								editor._.contentDomReady = contentDomReady;
220
221 								// We need to recreate the iframe in FF for every
222 								// data load, otherwise the following spellcheck
223 								// and execCommand features will be active only for
224 								// the first time.
225 								// The same is valid for IE with custom domain,
226 								// because the iframe src must be reset every time.
227 								if ( isCustomDomain || CKEDITOR.env.gecko )
228 									createIFrame();
229
230 								// For custom domain in IE, the data loading is
231 								// done through the src attribute of the iframe.
232 								if ( !isCustomDomain )
233 								{
234 									var doc = iframe.$.contentWindow.document;
235 									doc.open();
236 									doc.write( data );
237 									doc.close();
238 								}
239 							},
240
241 							getData : function()
242 							{
243 								var data = editor.dataProcessor.toDataFormat( new CKEDITOR.dom.element( iframe.$.contentWindow.document.body ) );
244
245 								// Restore protected attributes.
246 								data = protectEventsRestore( data );
247
248 								return data;
249 							},
250
251 							getSnapshotData : function()
252 							{
253 								return iframe.$.contentWindow.document.body.innerHTML;
254 							},
255
256 							loadSnapshotData : function( data )
257 							{
258 								iframe.$.contentWindow.document.body.innerHTML = data;
259 							},
260
261 							unload : function( holderElement )
262 							{
263 								editor.window = editor.document = iframe = mainElement = isPendingFocus = null;
264
265 								editor.fire( 'contentDomUnload' );
266 							},
267
268 							focus : function()
269 							{
270 								if ( isLoadingData )
271 									isPendingFocus = true;
272 								else if ( editor.window )
273 									editor.window.focus();
274 							}
275 						});
276 				});
277 		}
278 	});
279 })();
280
281 /**
282  * Disables the ability of resize objects (image and tables) in the editing
283  * area
284  * @type Boolean
285  * @default false
286  * @example
287  * config.disableObjectResizing = true;
288  */
289 CKEDITOR.config.disableObjectResizing = false;
290
291 /**
292  * Disables the "table tools" offered natively by the browser (currently
293  * Firefox only) to make quick table editing operations, like adding or
294  * deleting rows and columns.
295  * @type Boolean
296  * @default true
297  * @example
298  * config.disableNativeTableHandles = false;
299  */
300 CKEDITOR.config.disableNativeTableHandles = true;
301
302 /**
303  * Disables the built-in spell checker while typing natively available in the
304  * browser (currently Firefox and Safari only).<br /><br />
305  *
306  * Even if word suggestions will not appear in the FCKeditor context menu, this
307  * feature is useful to help quickly identifying misspelled words.<br /><br />
308  *
309  * This setting is currently compatible with Firefox only due to limitations in
310  * other browsers.
311  * @type Boolean
312  * @default true
313  * @example
314  * config.disableNativeSpellChecker = false;
315  */
316 CKEDITOR.config.disableNativeSpellChecker = true;
317