diff options
Diffstat (limited to 'sources/adapters/jquery.js')
-rw-r--r-- | sources/adapters/jquery.js | 379 |
1 files changed, 379 insertions, 0 deletions
diff --git a/sources/adapters/jquery.js b/sources/adapters/jquery.js new file mode 100644 index 0000000..4a7796b --- /dev/null +++ b/sources/adapters/jquery.js | |||
@@ -0,0 +1,379 @@ | |||
1 | /** | ||
2 | * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved. | ||
3 | * For licensing, see LICENSE.md or http://ckeditor.com/license | ||
4 | */ | ||
5 | |||
6 | /** | ||
7 | * @fileOverview Defines the {@link CKEDITOR_Adapters.jQuery jQuery Adapter}. | ||
8 | */ | ||
9 | |||
10 | /** | ||
11 | * @class CKEDITOR_Adapters.jQuery | ||
12 | * @singleton | ||
13 | * | ||
14 | * The jQuery Adapter allows for easy use of basic CKEditor functions and access to the internal API. | ||
15 | * To find more information about the jQuery Adapter, go to the [jQuery Adapter section](#!/guide/dev_jquery) | ||
16 | * of the Developer's Guide or see the "Create Editors with jQuery" sample. | ||
17 | * | ||
18 | * @aside guide dev_jquery | ||
19 | */ | ||
20 | |||
21 | ( function( $ ) { | ||
22 | if ( typeof $ == 'undefined' ) { | ||
23 | throw new Error( 'jQuery should be loaded before CKEditor jQuery adapter.' ); | ||
24 | } | ||
25 | |||
26 | if ( typeof CKEDITOR == 'undefined' ) { | ||
27 | throw new Error( 'CKEditor should be loaded before CKEditor jQuery adapter.' ); | ||
28 | } | ||
29 | |||
30 | /** | ||
31 | * Allows CKEditor to override `jQuery.fn.val()`. When set to `true`, the `val()` function | ||
32 | * used on textarea elements replaced with CKEditor uses the CKEditor API. | ||
33 | * | ||
34 | * This configuration option is global and is executed during the loading of the jQuery Adapter. | ||
35 | * It cannot be customized across editor instances. | ||
36 | * | ||
37 | * Read more in the [documentation](#!/guide/dev_jquery). | ||
38 | * | ||
39 | * <script> | ||
40 | * CKEDITOR.config.jqueryOverrideVal = true; | ||
41 | * </script> | ||
42 | * | ||
43 | * <!-- Important: The jQuery Adapter is loaded *after* setting jqueryOverrideVal. --> | ||
44 | * <script src="/ckeditor/adapters/jquery.js"></script> | ||
45 | * | ||
46 | * <script> | ||
47 | * $( 'textarea' ).ckeditor(); | ||
48 | * // ... | ||
49 | * $( 'textarea' ).val( 'New content' ); | ||
50 | * </script> | ||
51 | * | ||
52 | * @cfg {Boolean} [jqueryOverrideVal=true] | ||
53 | * @member CKEDITOR.config | ||
54 | */ | ||
55 | CKEDITOR.config.jqueryOverrideVal = | ||
56 | typeof CKEDITOR.config.jqueryOverrideVal == 'undefined' ? true : CKEDITOR.config.jqueryOverrideVal; | ||
57 | |||
58 | // jQuery object methods. | ||
59 | $.extend( $.fn, { | ||
60 | /** | ||
61 | * Returns an existing CKEditor instance for the first matched element. | ||
62 | * Allows to easily use the internal API. Does not return a jQuery object. | ||
63 | * | ||
64 | * Raises an exception if the editor does not exist or is not ready yet. | ||
65 | * | ||
66 | * @returns CKEDITOR.editor | ||
67 | * @deprecated Use {@link #editor editor property} instead. | ||
68 | */ | ||
69 | ckeditorGet: function() { | ||
70 | var instance = this.eq( 0 ).data( 'ckeditorInstance' ); | ||
71 | |||
72 | if ( !instance ) | ||
73 | throw 'CKEditor is not initialized yet, use ckeditor() with a callback.'; | ||
74 | |||
75 | return instance; | ||
76 | }, | ||
77 | |||
78 | /** | ||
79 | * A jQuery function which triggers the creation of CKEditor with `<textarea>` and | ||
80 | * {@link CKEDITOR.dtd#$editable editable} elements. | ||
81 | * Every `<textarea>` element will be converted to a classic (`iframe`-based) editor, | ||
82 | * while any other supported element will be converted to an inline editor. | ||
83 | * This method binds the callback to the `instanceReady` event of all instances. | ||
84 | * If the editor has already been created, the callback is fired straightaway. | ||
85 | * You can also create multiple editors at once by using `$( '.className' ).ckeditor();`. | ||
86 | * | ||
87 | * **Note**: jQuery chaining and mixed parameter order is allowed. | ||
88 | * | ||
89 | * @param {Function} callback | ||
90 | * Function to be run on the editor instance. Callback takes the source element as a parameter. | ||
91 | * | ||
92 | * $( 'textarea' ).ckeditor( function( textarea ) { | ||
93 | * // Callback function code. | ||
94 | * } ); | ||
95 | * | ||
96 | * @param {Object} config | ||
97 | * Configuration options for new instance(s) if not already created. | ||
98 | * | ||
99 | * $( 'textarea' ).ckeditor( { | ||
100 | * uiColor: '#9AB8F3' | ||
101 | * } ); | ||
102 | * | ||
103 | * @returns jQuery.fn | ||
104 | */ | ||
105 | ckeditor: function( callback, config ) { | ||
106 | if ( !CKEDITOR.env.isCompatible ) | ||
107 | throw new Error( 'The environment is incompatible.' ); | ||
108 | |||
109 | // Reverse the order of arguments if the first one isn't a function. | ||
110 | if ( !$.isFunction( callback ) ) { | ||
111 | var tmp = config; | ||
112 | config = callback; | ||
113 | callback = tmp; | ||
114 | } | ||
115 | |||
116 | // An array of instanceReady callback promises. | ||
117 | var promises = []; | ||
118 | |||
119 | config = config || {}; | ||
120 | |||
121 | // Iterate over the collection. | ||
122 | this.each( function() { | ||
123 | var $element = $( this ), | ||
124 | editor = $element.data( 'ckeditorInstance' ), | ||
125 | instanceLock = $element.data( '_ckeditorInstanceLock' ), | ||
126 | element = this, | ||
127 | dfd = new $.Deferred(); | ||
128 | |||
129 | promises.push( dfd.promise() ); | ||
130 | |||
131 | if ( editor && !instanceLock ) { | ||
132 | if ( callback ) | ||
133 | callback.apply( editor, [ this ] ); | ||
134 | |||
135 | dfd.resolve(); | ||
136 | } else if ( !instanceLock ) { | ||
137 | // CREATE NEW INSTANCE | ||
138 | |||
139 | // Handle config.autoUpdateElement inside this plugin if desired. | ||
140 | if ( config.autoUpdateElement || ( typeof config.autoUpdateElement == 'undefined' && CKEDITOR.config.autoUpdateElement ) ) { | ||
141 | config.autoUpdateElementJquery = true; | ||
142 | } | ||
143 | |||
144 | // Always disable config.autoUpdateElement. | ||
145 | config.autoUpdateElement = false; | ||
146 | $element.data( '_ckeditorInstanceLock', true ); | ||
147 | |||
148 | // Set instance reference in element's data. | ||
149 | if ( $( this ).is( 'textarea' ) ) | ||
150 | editor = CKEDITOR.replace( element, config ); | ||
151 | else | ||
152 | editor = CKEDITOR.inline( element, config ); | ||
153 | |||
154 | $element.data( 'ckeditorInstance', editor ); | ||
155 | |||
156 | // Register callback. | ||
157 | editor.on( 'instanceReady', function( evt ) { | ||
158 | var editor = evt.editor; | ||
159 | |||
160 | setTimeout( function() { | ||
161 | // Delay bit more if editor is still not ready. | ||
162 | if ( !editor.element ) { | ||
163 | setTimeout( arguments.callee, 100 ); | ||
164 | return; | ||
165 | } | ||
166 | |||
167 | // Remove this listener. Triggered when new instance is ready. | ||
168 | evt.removeListener(); | ||
169 | |||
170 | /** | ||
171 | * Forwards the CKEditor {@link CKEDITOR.editor#event-dataReady dataReady event} as a jQuery event. | ||
172 | * | ||
173 | * @event dataReady | ||
174 | * @param {CKEDITOR.editor} editor Editor instance. | ||
175 | */ | ||
176 | editor.on( 'dataReady', function() { | ||
177 | $element.trigger( 'dataReady.ckeditor', [ editor ] ); | ||
178 | } ); | ||
179 | |||
180 | /** | ||
181 | * Forwards the CKEditor {@link CKEDITOR.editor#event-setData setData event} as a jQuery event. | ||
182 | * | ||
183 | * @event setData | ||
184 | * @param {CKEDITOR.editor} editor Editor instance. | ||
185 | * @param data | ||
186 | * @param {String} data.dataValue The data that will be used. | ||
187 | */ | ||
188 | editor.on( 'setData', function( evt ) { | ||
189 | $element.trigger( 'setData.ckeditor', [ editor, evt.data ] ); | ||
190 | } ); | ||
191 | |||
192 | /** | ||
193 | * Forwards the CKEditor {@link CKEDITOR.editor#event-getData getData event} as a jQuery event. | ||
194 | * | ||
195 | * @event getData | ||
196 | * @param {CKEDITOR.editor} editor Editor instance. | ||
197 | * @param data | ||
198 | * @param {String} data.dataValue The data that will be returned. | ||
199 | */ | ||
200 | editor.on( 'getData', function( evt ) { | ||
201 | $element.trigger( 'getData.ckeditor', [ editor, evt.data ] ); | ||
202 | }, 999 ); | ||
203 | |||
204 | /** | ||
205 | * Forwards the CKEditor {@link CKEDITOR.editor#event-destroy destroy event} as a jQuery event. | ||
206 | * | ||
207 | * @event destroy | ||
208 | * @param {CKEDITOR.editor} editor Editor instance. | ||
209 | */ | ||
210 | editor.on( 'destroy', function() { | ||
211 | $element.trigger( 'destroy.ckeditor', [ editor ] ); | ||
212 | } ); | ||
213 | |||
214 | // Overwrite save button to call jQuery submit instead of javascript submit. | ||
215 | // Otherwise jQuery.forms does not work properly | ||
216 | editor.on( 'save', function() { | ||
217 | $( element.form ).submit(); | ||
218 | return false; | ||
219 | }, null, null, 20 ); | ||
220 | |||
221 | // Integrate with form submit. | ||
222 | if ( editor.config.autoUpdateElementJquery && $element.is( 'textarea' ) && $( element.form ).length ) { | ||
223 | var onSubmit = function() { | ||
224 | $element.ckeditor( function() { | ||
225 | editor.updateElement(); | ||
226 | } ); | ||
227 | }; | ||
228 | |||
229 | // Bind to submit event. | ||
230 | $( element.form ).submit( onSubmit ); | ||
231 | |||
232 | // Bind to form-pre-serialize from jQuery Forms plugin. | ||
233 | $( element.form ).bind( 'form-pre-serialize', onSubmit ); | ||
234 | |||
235 | // Unbind when editor destroyed. | ||
236 | $element.bind( 'destroy.ckeditor', function() { | ||
237 | $( element.form ).unbind( 'submit', onSubmit ); | ||
238 | $( element.form ).unbind( 'form-pre-serialize', onSubmit ); | ||
239 | } ); | ||
240 | } | ||
241 | |||
242 | // Garbage collect on destroy. | ||
243 | editor.on( 'destroy', function() { | ||
244 | $element.removeData( 'ckeditorInstance' ); | ||
245 | } ); | ||
246 | |||
247 | // Remove lock. | ||
248 | $element.removeData( '_ckeditorInstanceLock' ); | ||
249 | |||
250 | /** | ||
251 | * Forwards the CKEditor {@link CKEDITOR.editor#event-instanceReady instanceReady event} as a jQuery event. | ||
252 | * | ||
253 | * @event instanceReady | ||
254 | * @param {CKEDITOR.editor} editor Editor instance. | ||
255 | */ | ||
256 | $element.trigger( 'instanceReady.ckeditor', [ editor ] ); | ||
257 | |||
258 | // Run given (first) code. | ||
259 | if ( callback ) | ||
260 | callback.apply( editor, [ element ] ); | ||
261 | |||
262 | dfd.resolve(); | ||
263 | }, 0 ); | ||
264 | }, null, null, 9999 ); | ||
265 | } else { | ||
266 | // Editor is already during creation process, bind our code to the event. | ||
267 | editor.once( 'instanceReady', function() { | ||
268 | setTimeout( function() { | ||
269 | // Delay bit more if editor is still not ready. | ||
270 | if ( !editor.element ) { | ||
271 | setTimeout( arguments.callee, 100 ); | ||
272 | return; | ||
273 | } | ||
274 | |||
275 | // Run given code. | ||
276 | if ( editor.element.$ == element && callback ) | ||
277 | callback.apply( editor, [ element ] ); | ||
278 | |||
279 | dfd.resolve(); | ||
280 | }, 0 ); | ||
281 | }, null, null, 9999 ); | ||
282 | } | ||
283 | } ); | ||
284 | |||
285 | /** | ||
286 | * The [jQuery Promise object]((http://api.jquery.com/promise/)) that handles the asynchronous constructor. | ||
287 | * This promise will be resolved after **all** of the constructors. | ||
288 | * | ||
289 | * @property {Function} promise | ||
290 | */ | ||
291 | var dfd = new $.Deferred(); | ||
292 | |||
293 | this.promise = dfd.promise(); | ||
294 | |||
295 | $.when.apply( this, promises ).then( function() { | ||
296 | dfd.resolve(); | ||
297 | } ); | ||
298 | |||
299 | /** | ||
300 | * Existing CKEditor instance. Allows to easily use the internal API. | ||
301 | * | ||
302 | * **Note**: This is not a jQuery object. | ||
303 | * | ||
304 | * var editor = $( 'textarea' ).ckeditor().editor; | ||
305 | * | ||
306 | * @property {CKEDITOR.editor} editor | ||
307 | */ | ||
308 | this.editor = this.eq( 0 ).data( 'ckeditorInstance' ); | ||
309 | |||
310 | return this; | ||
311 | } | ||
312 | } ); | ||
313 | |||
314 | /** | ||
315 | * Overwritten jQuery `val()` method for `<textarea>` elements that have bound CKEditor instances. | ||
316 | * This method gets or sets editor content by using the {@link CKEDITOR.editor#method-getData editor.getData()} | ||
317 | * or {@link CKEDITOR.editor#method-setData editor.setData()} methods. To handle | ||
318 | * the {@link CKEDITOR.editor#method-setData editor.setData()} callback (as `setData` is asynchronous), | ||
319 | * `val( 'some data' )` will return a [jQuery Promise object](http://api.jquery.com/promise/). | ||
320 | * | ||
321 | * @method val | ||
322 | * @returns String|Number|Array|jQuery.fn|function(jQuery Promise) | ||
323 | */ | ||
324 | if ( CKEDITOR.config.jqueryOverrideVal ) { | ||
325 | $.fn.val = CKEDITOR.tools.override( $.fn.val, function( oldValMethod ) { | ||
326 | return function( value ) { | ||
327 | // Setter, i.e. .val( "some data" ); | ||
328 | if ( arguments.length ) { | ||
329 | var _this = this, | ||
330 | promises = [], //use promise to handle setData callback | ||
331 | |||
332 | result = this.each( function() { | ||
333 | var $elem = $( this ), | ||
334 | editor = $elem.data( 'ckeditorInstance' ); | ||
335 | |||
336 | // Handle .val for CKEditor. | ||
337 | if ( $elem.is( 'textarea' ) && editor ) { | ||
338 | var dfd = new $.Deferred(); | ||
339 | |||
340 | editor.setData( value, function() { | ||
341 | dfd.resolve(); | ||
342 | } ); | ||
343 | |||
344 | promises.push( dfd.promise() ); | ||
345 | return true; | ||
346 | // Call default .val function for rest of elements | ||
347 | } else { | ||
348 | return oldValMethod.call( $elem, value ); | ||
349 | } | ||
350 | } ); | ||
351 | |||
352 | // If there is no promise return default result (jQuery object of chaining). | ||
353 | if ( !promises.length ) | ||
354 | return result; | ||
355 | // Create one promise which will be resolved when all of promises will be done. | ||
356 | else { | ||
357 | var dfd = new $.Deferred(); | ||
358 | |||
359 | $.when.apply( this, promises ).done( function() { | ||
360 | dfd.resolveWith( _this ); | ||
361 | } ); | ||
362 | |||
363 | return dfd.promise(); | ||
364 | } | ||
365 | } | ||
366 | // Getter .val(); | ||
367 | else { | ||
368 | var $elem = $( this ).eq( 0 ), | ||
369 | editor = $elem.data( 'ckeditorInstance' ); | ||
370 | |||
371 | if ( $elem.is( 'textarea' ) && editor ) | ||
372 | return editor.getData(); | ||
373 | else | ||
374 | return oldValMethod.call( $elem ); | ||
375 | } | ||
376 | }; | ||
377 | } ); | ||
378 | } | ||
379 | } )( window.jQuery ); | ||