1 /* 2 Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 /** 7 * @fileOverview jQuery adapter provides easy use of basic CKEditor functions 8 * and access to internal API. It also integrates some aspects of CKEditor with 9 * jQuery framework. 10 * 11 * Every TEXTAREA, DIV and P elements can be converted to working editor. 12 * 13 * Plugin exposes some of editor's event to jQuery event system. All of those are namespaces inside 14 * ".ckeditor" namespace and can be binded/listened on supported textarea, div and p nodes. 15 * 16 * Available jQuery events: 17 * - instanceReady.ckeditor( editor, rootNode ) 18 * Triggered when new instance is ready. 19 * - destroy.ckeditor( editor ) 20 * Triggered when instance is destroyed. 21 * - getData.ckeditor( editor, eventData ) 22 * Triggered when getData event is fired inside editor. It can change returned data using eventData reference. 23 * - setData.ckeditor( editor ) 24 * Triggered when getData event is fired inside editor. 25 * 26 * @example 27 * <script src="jquery.js"></script> 28 * <script src="ckeditor.js"></script> 29 * <script src="adapters/jquery/adapter.js"></script> 30 */ 31 32 (function() 33 { 34 /** 35 * Allows CKEditor to override jQuery.fn.val(), making it possible to use the val() 36 * function on textareas, as usual, having it synchronized with CKEditor.<br> 37 * <br> 38 * This configuration option is global and executed during the jQuery Adapter loading. 39 * It can't be customized across editor instances. 40 * @type Boolean 41 * @example 42 * <script> 43 * CKEDITOR.config.jqueryOverrideVal = true; 44 * </script> 45 * <!-- Important: The JQuery adapter is loaded *after* setting jqueryOverrideVal --> 46 * <script src="/ckeditor/adapters/jquery.js"></script> 47 * @example 48 * // ... then later in the code ... 49 * 50 * $( 'textarea' ).ckeditor(); 51 * // ... 52 * $( 'textarea' ).val( 'New content' ); 53 */ 54 CKEDITOR.config.jqueryOverrideVal = typeof CKEDITOR.config.jqueryOverrideVal == 'undefined' 55 ? true : CKEDITOR.config.jqueryOverrideVal; 56 57 var jQuery = window.jQuery; 58 59 if ( typeof jQuery == 'undefined' ) 60 return; 61 62 // jQuery object methods. 63 jQuery.extend( jQuery.fn, 64 /** @lends jQuery.fn */ 65 { 66 /** 67 * Return existing CKEditor instance for first matched element. 68 * Allows to easily use internal API. Doesn't return jQuery object. 69 * 70 * Raised exception if editor doesn't exist or isn't ready yet. 71 * 72 * @name jQuery.ckeditorGet 73 * @return CKEDITOR.editor 74 * @see CKEDITOR.editor 75 */ 76 ckeditorGet: function() 77 { 78 var instance = this.eq( 0 ).data( 'ckeditorInstance' ); 79 if ( !instance ) 80 throw "CKEditor not yet initialized, use ckeditor() with callback."; 81 return instance; 82 }, 83 /** 84 * Triggers creation of CKEditor in all matched elements (reduced to DIV, P and TEXTAREAs). 85 * Binds callback to instanceReady event of all instances. If editor is already created, than 86 * callback is fired right away. 87 * 88 * Mixed parameter order allowed. 89 * 90 * @param callback Function to be run on editor instance. Passed parameters: [ textarea ]. 91 * Callback is fiered in "this" scope being ckeditor instance and having source textarea as first param. 92 * 93 * @param config Configuration options for new instance(s) if not already created. 94 * See URL 95 * 96 * @example 97 * $( 'textarea' ).ckeditor( function( textarea ) { 98 * $( textarea ).val( this.getData() ) 99 * } ); 100 * 101 * @name jQuery.fn.ckeditor 102 * @return jQuery.fn 103 */ 104 ckeditor: function( callback, config ) 105 { 106 if ( !CKEDITOR.env.isCompatible ) 107 return this; 108 109 if ( !jQuery.isFunction( callback )) 110 { 111 var tmp = config; 112 config = callback; 113 callback = tmp; 114 } 115 config = config || {}; 116 117 this.filter( 'textarea, div, p' ).each( function() 118 { 119 var $element = jQuery( this ), 120 editor = $element.data( 'ckeditorInstance' ), 121 instanceLock = $element.data( '_ckeditorInstanceLock' ), 122 element = this; 123 124 if ( editor && !instanceLock ) 125 { 126 if ( callback ) 127 callback.apply( editor, [ this ] ); 128 } 129 else if ( !instanceLock ) 130 { 131 // CREATE NEW INSTANCE 132 133 // Handle config.autoUpdateElement inside this plugin if desired. 134 if ( config.autoUpdateElement 135 || ( typeof config.autoUpdateElement == 'undefined' && CKEDITOR.config.autoUpdateElement ) ) 136 { 137 config.autoUpdateElementJquery = true; 138 } 139 140 // Always disable config.autoUpdateElement. 141 config.autoUpdateElement = false; 142 $element.data( '_ckeditorInstanceLock', true ); 143 144 // Set instance reference in element's data. 145 editor = CKEDITOR.replace( element, config ); 146 $element.data( 'ckeditorInstance', editor ); 147 148 // Register callback. 149 editor.on( 'instanceReady', function( event ) 150 { 151 var editor = event.editor; 152 setTimeout( function() 153 { 154 // Delay bit more if editor is still not ready. 155 if ( !editor.element ) 156 { 157 setTimeout( arguments.callee, 100 ); 158 return; 159 } 160 161 // Remove this listener. 162 event.removeListener( 'instanceReady', this.callee ); 163 164 // Forward setData on dataReady. 165 editor.on( 'dataReady', function() 166 { 167 $element.trigger( 'setData' + '.ckeditor', [ editor ] ); 168 }); 169 170 // Forward getData. 171 editor.on( 'getData', function( event ) { 172 $element.trigger( 'getData' + '.ckeditor', [ editor, event.data ] ); 173 }, 999 ); 174 175 // Forward destroy event. 176 editor.on( 'destroy', function() 177 { 178 $element.trigger( 'destroy.ckeditor', [ editor ] ); 179 }); 180 181 // Integrate with form submit. 182 if ( editor.config.autoUpdateElementJquery && $element.is( 'textarea' ) && $element.parents( 'form' ).length ) 183 { 184 var onSubmit = function() 185 { 186 $element.ckeditor( function() 187 { 188 editor.updateElement(); 189 }); 190 }; 191 192 // Bind to submit event. 193 $element.parents( 'form' ).submit( onSubmit ); 194 195 // Bind to form-pre-serialize from jQuery Forms plugin. 196 $element.parents( 'form' ).bind( 'form-pre-serialize', onSubmit ); 197 198 // Unbind when editor destroyed. 199 $element.bind( 'destroy.ckeditor', function() 200 { 201 $element.parents( 'form' ).unbind( 'submit', onSubmit ); 202 $element.parents( 'form' ).unbind( 'form-pre-serialize', onSubmit ); 203 }); 204 } 205 206 // Garbage collect on destroy. 207 editor.on( 'destroy', function() 208 { 209 $element.data( 'ckeditorInstance', null ); 210 }); 211 212 // Remove lock. 213 $element.data( '_ckeditorInstanceLock', null ); 214 215 // Fire instanceReady event. 216 $element.trigger( 'instanceReady.ckeditor', [ editor ] ); 217 218 // Run given (first) code. 219 if ( callback ) 220 callback.apply( editor, [ element ] ); 221 }, 0 ); 222 }, null, null, 9999); 223 } 224 else 225 { 226 // Editor is already during creation process, bind our code to the event. 227 CKEDITOR.on( 'instanceReady', function( event ) 228 { 229 var editor = event.editor; 230 setTimeout( function() 231 { 232 // Delay bit more if editor is still not ready. 233 if ( !editor.element ) 234 { 235 setTimeout( arguments.callee, 100 ); 236 return; 237 } 238 239 if ( editor.element.$ == element ) 240 { 241 // Run given code. 242 if ( callback ) 243 callback.apply( editor, [ element ] ); 244 } 245 }, 0 ); 246 }, null, null, 9999); 247 } 248 }); 249 return this; 250 } 251 }); 252 253 // New val() method for objects. 254 if ( CKEDITOR.config.jqueryOverrideVal ) 255 { 256 jQuery.fn.val = CKEDITOR.tools.override( jQuery.fn.val, function( oldValMethod ) 257 { 258 /** 259 * CKEditor-aware val() method. 260 * 261 * Acts same as original jQuery val(), but for textareas which have CKEditor instances binded to them, method 262 * returns editor's content. It also works for settings values. 263 * 264 * @param oldValMethod 265 * @name jQuery.fn.val 266 */ 267 return function( newValue, forceNative ) 268 { 269 var isSetter = typeof newValue != 'undefined', 270 result; 271 272 this.each( function() 273 { 274 var $this = jQuery( this ), 275 editor = $this.data( 'ckeditorInstance' ); 276 277 if ( !forceNative && $this.is( 'textarea' ) && editor ) 278 { 279 if ( isSetter ) 280 editor.setData( newValue ); 281 else 282 { 283 result = editor.getData(); 284 // break; 285 return null; 286 } 287 } 288 else 289 { 290 if ( isSetter ) 291 oldValMethod.call( $this, newValue ); 292 else 293 { 294 result = oldValMethod.call( $this ); 295 // break; 296 return null; 297 } 298 } 299 300 return true; 301 }); 302 return isSetter ? this : result; 303 }; 304 }); 305 } 306 })(); 307