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