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 /**
  7  * @fileOverview The "wysiwygarea" plugin. It registers the "wysiwyg" editing
  8  *		mode, which handles the main editing area space.
  9  */
 10
 11 (function()
 12 {
 13 	function onInsertHtml( evt )
 14 	{
 15 		if ( this.mode == 'wysiwyg' )
 16 		{
 17 			var $doc = this.document.$,
 18 				data = evt.data;
 19
 20 			if ( this.dataProcessor )
 21 				data = this.dataProcessor.toHtml( data );
 22
 23 			if ( CKEDITOR.env.ie )
 24 				$doc.selection.createRange().pasteHTML( data );
 25 			else
 26 				$doc.execCommand( 'inserthtml', false, data );
 27 		}
 28 	}
 29
 30 	function onInsertElement( evt )
 31 	{
 32 		if ( this.mode == 'wysiwyg' )
 33 		{
 34 			var element = evt.data,
 35 				elementName = element.getName(),
 36 				isBlock = CKEDITOR.dtd.$block[ elementName ];
 37
 38 			var selection = this.getSelection(),
 39 				ranges = selection.getRanges();
 40
 41 			var range, clone, lastElement, bookmark;
 42
 43 			for ( var i = ranges.length - 1 ; i >= 0 ; i-- )
 44 			{
 45 				range = ranges[ i ];
 46
 47 				// Remove the original contents.
 48 				range.deleteContents();
 49
 50 				clone = !i && element || element.clone( true );
 51
 52 				var toSplit;
 53
 54 				// If the new node is a block element, split the current block (if any).
 55 				if ( this.config.enterMode != CKEDITOR.ENTER_BR && isBlock )
 56 				{
 57 					var startPath = new CKEDITOR.dom.elementPath( range.startContainer ),
 58 						j = 0,
 59 						parent;
 60
 61 					while( ( parent = startPath.elements[ j++ ] ) && parent != startPath.blockLimit )
 62 					{
 63 						var parentName = parent.getName(),
 64 							parentDtd = CKEDITOR.dtd[ parentName ];
 65
 66 						if ( parentDtd && !parentDtd[ elementName ] )
 67 							toSplit = parent;
 68 					}
 69 				}
 70
 71 				// Insert the new node.
 72 				range.insertNode( clone );
 73
 74 				if ( toSplit )
 75 					clone.breakParent( toSplit );
 76
 77 				// Save the last element reference so we can make the
 78 				// selection later.
 79 				if ( !lastElement )
 80 					lastElement = clone;
 81 			}
 82
 83 			range.moveToPosition( lastElement, CKEDITOR.POSITION_AFTER_END );
 84
 85 			var next = lastElement.getNextSourceNode( true );
 86 			if ( next && next.type == CKEDITOR.NODE_ELEMENT )
 87 				range.moveToElementEditStart( next );
 88
 89 			selection.selectRanges( [ range ] );
 90 		}
 91 	}
 92
 93 	CKEDITOR.plugins.add( 'wysiwygarea',
 94 	{
 95 		requires : [ 'editingblock' ],
 96
 97 		init : function( editor )
 98 		{
 99 			editor.on( 'editingBlockReady', function()
100 				{
101 					var mainElement,
102 						iframe,
103 						isLoadingData,
104 						isPendingFocus,
105 						fireMode;
106
107 					// The following information is needed for IE only.
108 					var isCustomDomain = CKEDITOR.env.ie && document.domain != window.location.hostname;
109
110 					// Creates the iframe that holds the editable document.
111 					var createIFrame = function()
112 					{
113 						if ( iframe )
114 							iframe.remove();
115
116 						iframe = new CKEDITOR.dom.element( 'iframe' )
117 							.setAttributes({
118 								frameBorder : 0,
119 								tabIndex : -1,
120 								allowTransparency : true })
121 							.setStyles({
122 								width : '100%',
123 								height : '100%' });
124
125 						if ( CKEDITOR.env.ie )
126 						{
127 							if ( isCustomDomain )
128 							{
129 								// The document domain must be set within the src
130 								// attribute.
131 								iframe.setAttribute( 'src',
132 									'javascript:void( (function(){' +
133 										'document.open();' +
134 										'document.domain="' + document.domain + '";' +
135 										'document.write( window.parent._cke_htmlToLoad_' + editor.name + ' );' +
136 										'document.close();' +
137 										'window.parent._cke_htmlToLoad_' + editor.name + ' = null;' +
138 									'})() )' );
139 							}
140 							else
141 								// To avoid HTTPS warnings.
142 								iframe.setAttribute( 'src', 'javascript:void(0)' );
143 						}
144
145 						// Append the new IFRAME to the main element. For IE, it
146 						// must be done after setting the "src", to avoid the
147 						// "secure/unsecure" message under HTTPS.
148 						mainElement.append( iframe );
149
150
151 						if ( CKEDITOR.env.gecko )
152 						{
153 							// Accessibility attributes for Firefox.
154 							mainElement.setAttributes(
155 								{
156 									role : 'region',
157 									title : 'CKEditor ' + editor.name + '. Type in text.'
158 								} );
159 							iframe.setAttributes(
160 								{
161 									role : 'region',
162 									title : ' '
163 								} );
164 						}
165 						else if ( CKEDITOR.env.ie )
166 						{
167 							// Accessibility label for IE.
168 							var label = CKEDITOR.document.createElement( 'label' );
169 							label.setStyles( {
170 								position : 'absolute',
171 								'top' : '-1000000px',
172 								left : '-1000000px'
173 							} );
174 							label.append( CKEDITOR.document.createText( 'CKEditor ' + editor.name ) );
175 							label.insertBefore( iframe );
176 						}
177 					};
178
179 					// The script that is appended to the data being loaded. It
180 					// enables editing, and makes some
181 					var activationScript =
182 						'<script id="cke_actscrpt" type="text/javascript">' +
183 							'window.onload = function()' +
184 							'{' +
185 								// Remove this script from the DOM.
186 								'var s = document.getElementById( "cke_actscrpt" );' +
187 								's.parentNode.removeChild( s );' +
188
189 								// Call the temporary function for the editing
190 								// boostrap.
191 								'window.parent.CKEDITOR._.contentDomReady' + editor.name + '( window );' +
192 							'}' +
193 						'</script>';
194
195 					// Editing area bootstrap code.
196 					var contentDomReady = function( domWindow )
197 					{
198 						delete CKEDITOR._[ 'contentDomReady' + editor.name ];
199
200 						var domDocument = domWindow.document,
201 							body = domDocument.body;
202
203 						body.spellcheck = !editor.config.disableNativeSpellChecker;
204
205 						if ( CKEDITOR.env.ie )
206 						{
207 							// Don't display the focus border.
208 							body.hideFocus = true;
209
210 							// Disable and re-enable the body to avoid IE from
211 							// taking the editing focus at startup. (#141 / #523)
212 							body.disabled = true;
213 							body.contentEditable = true;
214 							body.removeAttribute( 'disabled' );
215 						}
216 						else
217 							domDocument.designMode = 'on';
218
219 						// IE, Opera and Safari may not support it and throw
220 						// errors.
221 						try { domDocument.execCommand( 'enableObjectResizing', false, !editor.config.disableObjectResizing ) ; } catch(e) {}
222 						try { domDocument.execCommand( 'enableInlineTableEditing', false, !editor.config.disableNativeTableHandles ) ; } catch(e) {}
223
224 						domWindow	= editor.window		= new CKEDITOR.dom.window( domWindow );
225 						domDocument	= editor.document	= new CKEDITOR.dom.document( domDocument );
226
227 						var focusTarget = ( CKEDITOR.env.ie || CKEDITOR.env.safari ) ?
228 								domWindow : domDocument;
229
230 						focusTarget.on( 'blur', function()
231 							{
232 								editor.focusManager.blur();
233 							});
234
235 						focusTarget.on( 'focus', function()
236 							{
237 								editor.focusManager.focus();
238 							});
239
240 						var keystrokeHandler = editor.keystrokeHandler;
241 						if ( keystrokeHandler )
242 							keystrokeHandler.attach( domDocument );
243
244 						// Adds the document body as a context menu target.
245 						if ( editor.contextMenu )
246 							editor.contextMenu.addTarget( domDocument );
247
248 						setTimeout( function()
249 							{
250 								editor.fire( 'contentDom' );
251
252 								if ( fireMode )
253 								{
254 									editor.mode = 'wysiwyg';
255 									editor.fire( 'mode' );
256 									fireMode = false;
257 								}
258
259 								isLoadingData = false;
260
261 								if ( isPendingFocus )
262 								{
263 									editor.focus();
264 									isPendingFocus = false;
265 								}
266 							},
267 							0 );
268 					};
269
270 					editor.addMode( 'wysiwyg',
271 						{
272 							load : function( holderElement, data, isSnapshot )
273 							{
274 								mainElement = holderElement;
275
276 								// Create the iframe at load for all browsers
277 								// except FF and IE with custom domain.
278 								if ( !isCustomDomain || !CKEDITOR.env.gecko )
279 									createIFrame();
280
281 								// The editor data "may be dirty" after this
282 								// point.
283 								editor.mayBeDirty = true;
284
285 								fireMode = true;
286
287 								if ( isSnapshot )
288 									this.loadSnapshotData( data );
289 								else
290 									this.loadData( data );
291 							},
292
293 							loadData : function( data )
294 							{
295 								isLoadingData = true;
296
297 								// Get the HTML version of the data.
298 								if ( editor.dataProcessor )
299 									data = editor.dataProcessor.toHtml( data, ( editor.config.enterMode != CKEDITOR.ENTER_BR ) );
300
301 								data =
302 									editor.config.docType +
303 									'<html dir="' + editor.config.contentsLangDirection + '">' +
304 									'<head>' +
305 										'<link href="' + editor.config.contentsCss + '" type="text/css" rel="stylesheet" _fcktemp="true"/>' +
306 										'<style type="text/css" _fcktemp="true">' +
307 											editor._.styles.join( '\n' ) +
308 										'</style>'+
309 									'</head>' +
310 									'<body>' +
311 										data +
312 									'</body>' +
313 									'</html>' +
314 									activationScript;
315
316 								// For custom domain in IE, set the global variable
317 								// that will temporarily hold the editor data. This
318 								// reference will be used in the ifram src.
319 								if ( isCustomDomain )
320 									window[ '_cke_htmlToLoad_' + editor.name ] = data;
321
322 								CKEDITOR._[ 'contentDomReady' + editor.name ] = contentDomReady;
323
324 								// We need to recreate the iframe in FF for every
325 								// data load, otherwise the following spellcheck
326 								// and execCommand features will be active only for
327 								// the first time.
328 								// The same is valid for IE with custom domain,
329 								// because the iframe src must be reset every time.
330 								if ( isCustomDomain || CKEDITOR.env.gecko )
331 									createIFrame();
332
333 								// For custom domain in IE, the data loading is
334 								// done through the src attribute of the iframe.
335 								if ( !isCustomDomain )
336 								{
337 									var doc = iframe.$.contentWindow.document;
338 									doc.open();
339 									doc.write( data );
340 									doc.close();
341 								}
342 							},
343
344 							getData : function()
345 							{
346 								var data = iframe.$.contentWindow.document.body.innerHTML;
347
348 								if ( editor.dataProcessor )
349 									data = editor.dataProcessor.toDataFormat( data, ( editor.config.enterMode != CKEDITOR.ENTER_BR ) );
350
351 								return data;
352 							},
353
354 							getSnapshotData : function()
355 							{
356 								return iframe.$.contentWindow.document.body.innerHTML;
357 							},
358
359 							loadSnapshotData : function( data )
360 							{
361 								iframe.$.contentWindow.document.body.innerHTML = data;
362 							},
363
364 							unload : function( holderElement )
365 							{
366 								editor.window = editor.document = iframe = mainElement = isPendingFocus = null;
367
368 								editor.fire( 'contentDomUnload' );
369 							},
370
371 							focus : function()
372 							{
373 								if ( isLoadingData )
374 									isPendingFocus = true;
375 								else if ( editor.window )
376 								{
377 									editor.window.focus();
378 									editor.selectionChange();
379 								}
380 							}
381 						});
382
383 					editor.on( 'insertHtml', onInsertHtml, null, null, 20 );
384 					editor.on( 'insertElement', onInsertElement, null, null, 20 );
385 				});
386 		}
387 	});
388 })();
389
390 /**
391  * Disables the ability of resize objects (image and tables) in the editing
392  * area
393  * @type Boolean
394  * @default false
395  * @example
396  * config.disableObjectResizing = true;
397  */
398 CKEDITOR.config.disableObjectResizing = false;
399
400 /**
401  * Disables the "table tools" offered natively by the browser (currently
402  * Firefox only) to make quick table editing operations, like adding or
403  * deleting rows and columns.
404  * @type Boolean
405  * @default true
406  * @example
407  * config.disableNativeTableHandles = false;
408  */
409 CKEDITOR.config.disableNativeTableHandles = true;
410
411 /**
412  * Disables the built-in spell checker while typing natively available in the
413  * browser (currently Firefox and Safari only).<br /><br />
414  *
415  * Even if word suggestions will not appear in the FCKeditor context menu, this
416  * feature is useful to help quickly identifying misspelled words.<br /><br />
417  *
418  * This setting is currently compatible with Firefox only due to limitations in
419  * other browsers.
420  * @type Boolean
421  * @default true
422  * @example
423  * config.disableNativeSpellChecker = false;
424  */
425 CKEDITOR.config.disableNativeSpellChecker = true;
426