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