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