]> git.immae.eu Git - perso/Immae/Projets/packagist/piedsjaloux-ckeditor-component.git/blob - sources/core/editor.js
Add oembed
[perso/Immae/Projets/packagist/piedsjaloux-ckeditor-component.git] / sources / core / editor.js
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.editor} class that represents an
8 * editor instance.
9 */
10
11 ( function() {
12 // Override the basic constructor defined at editor_basic.js.
13 Editor.prototype = CKEDITOR.editor.prototype;
14 CKEDITOR.editor = Editor;
15
16 /**
17 * Represents an editor instance. This constructor should be rarely
18 * used in favor of the {@link CKEDITOR} editor creation functions.
19 *
20 * @class CKEDITOR.editor
21 * @mixins CKEDITOR.event
22 * @constructor Creates an editor class instance.
23 * @param {Object} [instanceConfig] Configuration values for this specific instance.
24 * @param {CKEDITOR.dom.element} [element] The DOM element upon which this editor
25 * will be created.
26 * @param {Number} [mode] The element creation mode to be used by this editor.
27 */
28 function Editor( instanceConfig, element, mode ) {
29 // Call the CKEDITOR.event constructor to initialize this instance.
30 CKEDITOR.event.call( this );
31
32 // Make a clone of the config object, to avoid having it touched by our code. (http://dev.ckeditor.com/ticket/9636)
33 instanceConfig = instanceConfig && CKEDITOR.tools.clone( instanceConfig );
34
35 // if editor is created off one page element.
36 if ( element !== undefined ) {
37 // Asserting element and mode not null.
38 if ( !( element instanceof CKEDITOR.dom.element ) )
39 throw new Error( 'Expect element of type CKEDITOR.dom.element.' );
40 else if ( !mode )
41 throw new Error( 'One of the element modes must be specified.' );
42
43 if ( CKEDITOR.env.ie && CKEDITOR.env.quirks && mode == CKEDITOR.ELEMENT_MODE_INLINE )
44 throw new Error( 'Inline element mode is not supported on IE quirks.' );
45
46 if ( !isSupportedElement( element, mode ) )
47 throw new Error( 'The specified element mode is not supported on element: "' + element.getName() + '".' );
48
49 /**
50 * The original host page element upon which the editor is created. It is only
51 * supposed to be provided by the particular editor creator and is not subject to
52 * be modified.
53 *
54 * @readonly
55 * @property {CKEDITOR.dom.element}
56 */
57 this.element = element;
58
59 /**
60 * This property indicates the way this instance is associated with the {@link #element}.
61 * See also {@link CKEDITOR#ELEMENT_MODE_INLINE} and {@link CKEDITOR#ELEMENT_MODE_REPLACE}.
62 *
63 * @readonly
64 * @property {Number}
65 */
66 this.elementMode = mode;
67
68 this.name = ( this.elementMode != CKEDITOR.ELEMENT_MODE_APPENDTO ) && ( element.getId() || element.getNameAtt() );
69 } else {
70 this.elementMode = CKEDITOR.ELEMENT_MODE_NONE;
71 }
72
73 // Declare the private namespace.
74 this._ = {};
75
76 this.commands = {};
77
78 /**
79 * Contains all UI templates created for this editor instance.
80 *
81 * @readonly
82 * @property {Object}
83 */
84 this.templates = {};
85
86 /**
87 * A unique identifier of this editor instance.
88 *
89 * **Note:** It will be originated from the `id` or `name`
90 * attribute of the {@link #element}, otherwise a name pattern of
91 * `'editor{n}'` will be used.
92 *
93 * @readonly
94 * @property {String}
95 */
96 this.name = this.name || genEditorName();
97
98 /**
99 * A unique random string assigned to each editor instance on the page.
100 *
101 * @readonly
102 * @property {String}
103 */
104 this.id = CKEDITOR.tools.getNextId();
105
106 /**
107 * Indicates editor initialization status. The following statuses are available:
108 *
109 * * **unloaded**: The initial state — the editor instance was initialized,
110 * but its components (configuration, plugins, language files) are not loaded yet.
111 * * **loaded**: The editor components were loaded — see the {@link CKEDITOR.editor#loaded} event.
112 * * **ready**: The editor is fully initialized and ready — see the {@link CKEDITOR.editor#instanceReady} event.
113 * * **destroyed**: The editor was destroyed — see the {@link CKEDITOR.editor#method-destroy} method.
114 *
115 * @since 4.1
116 * @readonly
117 * @property {String}
118 */
119 this.status = 'unloaded';
120
121 /**
122 * The configuration for this editor instance. It inherits all
123 * settings defined in {@link CKEDITOR.config}, combined with settings
124 * loaded from custom configuration files and those defined inline in
125 * the page when creating the editor.
126 *
127 * var editor = CKEDITOR.instances.editor1;
128 * alert( editor.config.skin ); // e.g. 'moono'
129 *
130 * @readonly
131 * @property {CKEDITOR.config}
132 */
133 this.config = CKEDITOR.tools.prototypedCopy( CKEDITOR.config );
134
135 /**
136 * The namespace containing UI features related to this editor instance.
137 *
138 * @readonly
139 * @property {CKEDITOR.ui}
140 */
141 this.ui = new CKEDITOR.ui( this );
142
143 /**
144 * Controls the focus state of this editor instance. This property
145 * is rarely used for normal API operations. It is mainly
146 * targeted at developers adding UI elements to the editor interface.
147 *
148 * @readonly
149 * @property {CKEDITOR.focusManager}
150 */
151 this.focusManager = new CKEDITOR.focusManager( this );
152
153 /**
154 * Controls keystroke typing in this editor instance.
155 *
156 * @readonly
157 * @property {CKEDITOR.keystrokeHandler}
158 */
159 this.keystrokeHandler = new CKEDITOR.keystrokeHandler( this );
160
161 // Make the editor update its command states on mode change.
162 this.on( 'readOnly', updateCommands );
163 this.on( 'selectionChange', function( evt ) {
164 updateCommandsContext( this, evt.data.path );
165 } );
166 this.on( 'activeFilterChange', function() {
167 updateCommandsContext( this, this.elementPath(), true );
168 } );
169 this.on( 'mode', updateCommands );
170
171 // Handle startup focus.
172 this.on( 'instanceReady', function() {
173 this.config.startupFocus && this.focus();
174 } );
175
176 CKEDITOR.fire( 'instanceCreated', null, this );
177
178 // Add this new editor to the CKEDITOR.instances collections.
179 CKEDITOR.add( this );
180
181 // Return the editor instance immediately to enable early stage event registrations.
182 CKEDITOR.tools.setTimeout( function() {
183 if ( this.status !== 'destroyed' ) {
184 initConfig( this, instanceConfig );
185 } else {
186 CKEDITOR.warn( 'editor-incorrect-destroy' );
187 }
188 }, 0, this );
189 }
190
191 var nameCounter = 0;
192
193 function genEditorName() {
194 do {
195 var name = 'editor' + ( ++nameCounter );
196 }
197 while ( CKEDITOR.instances[ name ] );
198
199 return name;
200 }
201
202 // Asserting element DTD depending on mode.
203 function isSupportedElement( element, mode ) {
204 if ( mode == CKEDITOR.ELEMENT_MODE_INLINE )
205 return element.is( CKEDITOR.dtd.$editable ) || element.is( 'textarea' );
206 else if ( mode == CKEDITOR.ELEMENT_MODE_REPLACE )
207 return !element.is( CKEDITOR.dtd.$nonBodyContent );
208 return 1;
209 }
210
211 function updateCommands() {
212 var commands = this.commands,
213 name;
214
215 for ( name in commands )
216 updateCommand( this, commands[ name ] );
217 }
218
219 function updateCommand( editor, cmd ) {
220 cmd[ cmd.startDisabled ? 'disable' : editor.readOnly && !cmd.readOnly ? 'disable' : cmd.modes[ editor.mode ] ? 'enable' : 'disable' ]();
221 }
222
223 function updateCommandsContext( editor, path, forceRefresh ) {
224 // Commands cannot be refreshed without a path. In edge cases
225 // it may happen that there's no selection when this function is executed.
226 // For example when active filter is changed in http://dev.ckeditor.com/ticket/10877.
227 if ( !path )
228 return;
229
230 var command,
231 name,
232 commands = editor.commands;
233
234 for ( name in commands ) {
235 command = commands[ name ];
236
237 if ( forceRefresh || command.contextSensitive )
238 command.refresh( editor, path );
239 }
240 }
241
242 // ##### START: Config Privates
243
244 // These function loads custom configuration files and cache the
245 // CKEDITOR.editorConfig functions defined on them, so there is no need to
246 // download them more than once for several instances.
247 var loadConfigLoaded = {};
248
249 function loadConfig( editor ) {
250 var customConfig = editor.config.customConfig;
251
252 // Check if there is a custom config to load.
253 if ( !customConfig )
254 return false;
255
256 customConfig = CKEDITOR.getUrl( customConfig );
257
258 var loadedConfig = loadConfigLoaded[ customConfig ] || ( loadConfigLoaded[ customConfig ] = {} );
259
260 // If the custom config has already been downloaded, reuse it.
261 if ( loadedConfig.fn ) {
262 // Call the cached CKEDITOR.editorConfig defined in the custom
263 // config file for the editor instance depending on it.
264 loadedConfig.fn.call( editor, editor.config );
265
266 // If there is no other customConfig in the chain, fire the
267 // "configLoaded" event.
268 if ( CKEDITOR.getUrl( editor.config.customConfig ) == customConfig || !loadConfig( editor ) )
269 editor.fireOnce( 'customConfigLoaded' );
270 } else {
271 // Load the custom configuration file.
272 // To resolve customConfig race conflicts, use scriptLoader#queue
273 // instead of scriptLoader#load (http://dev.ckeditor.com/ticket/6504).
274 CKEDITOR.scriptLoader.queue( customConfig, function() {
275 // If the CKEDITOR.editorConfig function has been properly
276 // defined in the custom configuration file, cache it.
277 if ( CKEDITOR.editorConfig )
278 loadedConfig.fn = CKEDITOR.editorConfig;
279 else
280 loadedConfig.fn = function() {};
281
282 // Call the load config again. This time the custom
283 // config is already cached and so it will get loaded.
284 loadConfig( editor );
285 } );
286 }
287
288 return true;
289 }
290
291 function initConfig( editor, instanceConfig ) {
292 // Setup the lister for the "customConfigLoaded" event.
293 editor.on( 'customConfigLoaded', function() {
294 if ( instanceConfig ) {
295 // Register the events that may have been set at the instance
296 // configuration object.
297 if ( instanceConfig.on ) {
298 for ( var eventName in instanceConfig.on ) {
299 editor.on( eventName, instanceConfig.on[ eventName ] );
300 }
301 }
302
303 // Overwrite the settings from the in-page config.
304 CKEDITOR.tools.extend( editor.config, instanceConfig, true );
305
306 delete editor.config.on;
307 }
308
309 onConfigLoaded( editor );
310 } );
311
312 // The instance config may override the customConfig setting to avoid
313 // loading the default ~/config.js file.
314 if ( instanceConfig && instanceConfig.customConfig != null )
315 editor.config.customConfig = instanceConfig.customConfig;
316
317 // Load configs from the custom configuration files.
318 if ( !loadConfig( editor ) )
319 editor.fireOnce( 'customConfigLoaded' );
320 }
321
322 // ##### END: Config Privates
323
324 // Set config related properties.
325 function onConfigLoaded( editor ) {
326 var config = editor.config;
327
328 /**
329 * Indicates the read-only state of this editor. This is a read-only property.
330 * See also {@link CKEDITOR.editor#setReadOnly}.
331 *
332 * @since 3.6
333 * @readonly
334 * @property {Boolean}
335 */
336 editor.readOnly = isEditorReadOnly();
337
338 function isEditorReadOnly() {
339 if ( config.readOnly ) {
340 return true;
341 }
342
343 if ( editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE ) {
344 if ( editor.element.is( 'textarea' ) ) {
345 return editor.element.hasAttribute( 'disabled' ) || editor.element.hasAttribute( 'readonly' );
346 } else {
347 return editor.element.isReadOnly();
348 }
349 } else if ( editor.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE ) {
350 return editor.element.hasAttribute( 'disabled' ) || editor.element.hasAttribute( 'readonly' );
351 }
352
353 return false;
354 }
355
356 /**
357 * Indicates that the editor is running in an environment where
358 * no block elements are accepted inside the content.
359 *
360 * This can be for example inline editor based on an `<h1>` element.
361 *
362 * @readonly
363 * @property {Boolean}
364 */
365 editor.blockless = editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE ?
366 !( editor.element.is( 'textarea' ) || CKEDITOR.dtd[ editor.element.getName() ].p ) :
367 false;
368
369 /**
370 * The [tabbing navigation](http://en.wikipedia.org/wiki/Tabbing_navigation) order determined for this editor instance.
371 * This can be set by the <code>{@link CKEDITOR.config#tabIndex}</code>
372 * setting or taken from the `tabindex` attribute of the
373 * {@link #element} associated with the editor.
374 *
375 * alert( editor.tabIndex ); // e.g. 0
376 *
377 * @readonly
378 * @property {Number} [=0]
379 */
380 editor.tabIndex = config.tabIndex || editor.element && editor.element.getAttribute( 'tabindex' ) || 0;
381
382 editor.activeEnterMode = editor.enterMode = validateEnterMode( editor, config.enterMode );
383 editor.activeShiftEnterMode = editor.shiftEnterMode = validateEnterMode( editor, config.shiftEnterMode );
384
385 // Set CKEDITOR.skinName. Note that it is not possible to have
386 // different skins on the same page, so the last one to set it "wins".
387 if ( config.skin )
388 CKEDITOR.skinName = config.skin;
389
390 // Fire the "configLoaded" event.
391 editor.fireOnce( 'configLoaded' );
392
393 initComponents( editor );
394 }
395
396 // Various other core components that read editor configuration.
397 function initComponents( editor ) {
398 // Documented in dataprocessor.js.
399 editor.dataProcessor = new CKEDITOR.htmlDataProcessor( editor );
400
401 // Set activeFilter directly to avoid firing event.
402 editor.filter = editor.activeFilter = new CKEDITOR.filter( editor );
403
404 loadSkin( editor );
405 }
406
407 function loadSkin( editor ) {
408 CKEDITOR.skin.loadPart( 'editor', function() {
409 loadLang( editor );
410 } );
411 }
412
413 function loadLang( editor ) {
414 CKEDITOR.lang.load( editor.config.language, editor.config.defaultLanguage, function( languageCode, lang ) {
415 var configTitle = editor.config.title;
416
417 /**
418 * The code for the language resources that have been loaded
419 * for the user interface elements of this editor instance.
420 *
421 * alert( editor.langCode ); // e.g. 'en'
422 *
423 * @readonly
424 * @property {String}
425 */
426 editor.langCode = languageCode;
427
428 /**
429 * An object that contains all language strings used by the editor interface.
430 *
431 * alert( editor.lang.basicstyles.bold ); // e.g. 'Negrito' (if the language is set to Portuguese)
432 *
433 * @readonly
434 * @property {Object} lang
435 */
436 // As we'll be adding plugin specific entries that could come
437 // from different language code files, we need a copy of lang,
438 // not a direct reference to it.
439 editor.lang = CKEDITOR.tools.prototypedCopy( lang );
440
441 /**
442 * Indicates the human-readable title of this editor. Although this is a read-only property,
443 * it can be initialized with {@link CKEDITOR.config#title}.
444 *
445 * **Note:** Please do not confuse this property with {@link CKEDITOR.editor#name editor.name}
446 * which identifies the instance in the {@link CKEDITOR#instances} literal.
447 *
448 * @since 4.2
449 * @readonly
450 * @property {String/Boolean}
451 */
452 editor.title = typeof configTitle == 'string' || configTitle === false ? configTitle : [ editor.lang.editor, editor.name ].join( ', ' );
453
454 if ( !editor.config.contentsLangDirection ) {
455 // Fallback to either the editable element direction or editor UI direction depending on creators.
456 editor.config.contentsLangDirection = editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE ? editor.element.getDirection( 1 ) : editor.lang.dir;
457 }
458
459 editor.fire( 'langLoaded' );
460
461 preloadStylesSet( editor );
462 } );
463 }
464
465 // Preloads styles set file (config.stylesSet).
466 // If stylesSet was defined directly (by an array)
467 // this function will call loadPlugins fully synchronously.
468 // If stylesSet is a string (path) loadPlugins will
469 // be called asynchronously.
470 // In both cases - styles will be preload before plugins initialization.
471 function preloadStylesSet( editor ) {
472 editor.getStylesSet( function( styles ) {
473 // Wait for editor#loaded, so plugins could add their listeners.
474 // But listen with high priority to fire editor#stylesSet before editor#uiReady and editor#setData.
475 editor.once( 'loaded', function() {
476 // Note: we can't use fireOnce because this event may canceled and fired again.
477 editor.fire( 'stylesSet', { styles: styles } );
478 }, null, null, 1 );
479
480 loadPlugins( editor );
481 } );
482 }
483
484 function loadPlugins( editor ) {
485 var config = editor.config,
486 plugins = config.plugins,
487 extraPlugins = config.extraPlugins,
488 removePlugins = config.removePlugins;
489
490 if ( extraPlugins ) {
491 // Remove them first to avoid duplications.
492 var extraRegex = new RegExp( '(?:^|,)(?:' + extraPlugins.replace( /\s*,\s*/g, '|' ) + ')(?=,|$)', 'g' );
493 plugins = plugins.replace( extraRegex, '' );
494
495 plugins += ',' + extraPlugins;
496 }
497
498 if ( removePlugins ) {
499 var removeRegex = new RegExp( '(?:^|,)(?:' + removePlugins.replace( /\s*,\s*/g, '|' ) + ')(?=,|$)', 'g' );
500 plugins = plugins.replace( removeRegex, '' );
501 }
502
503 // Load the Adobe AIR plugin conditionally.
504 CKEDITOR.env.air && ( plugins += ',adobeair' );
505
506 // Load all plugins defined in the "plugins" setting.
507 CKEDITOR.plugins.load( plugins.split( ',' ), function( plugins ) {
508 // The list of plugins.
509 var pluginsArray = [];
510
511 // The language code to get loaded for each plugin. Null
512 // entries will be appended for plugins with no language files.
513 var languageCodes = [];
514
515 // The list of URLs to language files.
516 var languageFiles = [];
517
518 /**
519 * An object that contains references to all plugins used by this
520 * editor instance.
521 *
522 * alert( editor.plugins.dialog.path ); // e.g. 'http://example.com/ckeditor/plugins/dialog/'
523 *
524 * // Check if a plugin is available.
525 * if ( editor.plugins.image ) {
526 * ...
527 * }
528 *
529 * @readonly
530 * @property {Object}
531 */
532 editor.plugins = plugins;
533
534 // Loop through all plugins, to build the list of language
535 // files to get loaded.
536 //
537 // Check also whether any of loaded plugins doesn't require plugins
538 // defined in config.removePlugins. Throw non-blocking error if this happens.
539 for ( var pluginName in plugins ) {
540 var plugin = plugins[ pluginName ],
541 pluginLangs = plugin.lang,
542 lang = null,
543 requires = plugin.requires,
544 match, name;
545
546 // Transform it into a string, if it's not one.
547 if ( CKEDITOR.tools.isArray( requires ) )
548 requires = requires.join( ',' );
549
550 if ( requires && ( match = requires.match( removeRegex ) ) ) {
551 while ( ( name = match.pop() ) ) {
552 CKEDITOR.error( 'editor-plugin-required', { plugin: name.replace( ',', '' ), requiredBy: pluginName } );
553 }
554 }
555
556 // If the plugin has "lang".
557 if ( pluginLangs && !editor.lang[ pluginName ] ) {
558 // Trasnform the plugin langs into an array, if it's not one.
559 if ( pluginLangs.split )
560 pluginLangs = pluginLangs.split( ',' );
561
562 // Resolve the plugin language. If the current language
563 // is not available, get English or the first one.
564 if ( CKEDITOR.tools.indexOf( pluginLangs, editor.langCode ) >= 0 )
565 lang = editor.langCode;
566 else {
567 // The language code may have the locale information (zh-cn).
568 // Fall back to locale-less in that case (zh).
569 var langPart = editor.langCode.replace( /-.*/, '' );
570 if ( langPart != editor.langCode && CKEDITOR.tools.indexOf( pluginLangs, langPart ) >= 0 )
571 lang = langPart;
572 // Try the only "generic" option we have: English.
573 else if ( CKEDITOR.tools.indexOf( pluginLangs, 'en' ) >= 0 )
574 lang = 'en';
575 else
576 lang = pluginLangs[ 0 ];
577 }
578
579 if ( !plugin.langEntries || !plugin.langEntries[ lang ] ) {
580 // Put the language file URL into the list of files to
581 // get downloaded.
582 languageFiles.push( CKEDITOR.getUrl( plugin.path + 'lang/' + lang + '.js' ) );
583 } else {
584 editor.lang[ pluginName ] = plugin.langEntries[ lang ];
585 lang = null;
586 }
587 }
588
589 // Save the language code, so we know later which
590 // language has been resolved to this plugin.
591 languageCodes.push( lang );
592
593 pluginsArray.push( plugin );
594 }
595
596 // Load all plugin specific language files in a row.
597 CKEDITOR.scriptLoader.load( languageFiles, function() {
598 // Initialize all plugins that have the "beforeInit" and "init" methods defined.
599 var methods = [ 'beforeInit', 'init', 'afterInit' ];
600 for ( var m = 0; m < methods.length; m++ ) {
601 for ( var i = 0; i < pluginsArray.length; i++ ) {
602 var plugin = pluginsArray[ i ];
603
604 // Uses the first loop to update the language entries also.
605 if ( m === 0 && languageCodes[ i ] && plugin.lang && plugin.langEntries )
606 editor.lang[ plugin.name ] = plugin.langEntries[ languageCodes[ i ] ];
607
608 // Call the plugin method (beforeInit and init).
609 if ( plugin[ methods[ m ] ] )
610 plugin[ methods[ m ] ]( editor );
611 }
612 }
613
614 editor.fireOnce( 'pluginsLoaded' );
615
616 // Setup the configured keystrokes.
617 config.keystrokes && editor.setKeystroke( editor.config.keystrokes );
618
619 // Setup the configured blocked keystrokes.
620 for ( i = 0; i < editor.config.blockedKeystrokes.length; i++ )
621 editor.keystrokeHandler.blockedKeystrokes[ editor.config.blockedKeystrokes[ i ] ] = 1;
622
623 editor.status = 'loaded';
624 editor.fireOnce( 'loaded' );
625 CKEDITOR.fire( 'instanceLoaded', null, editor );
626 } );
627 } );
628 }
629
630 // Send to data output back to editor's associated element.
631 function updateEditorElement() {
632 var element = this.element;
633
634 // Some editor creation mode will not have the
635 // associated element.
636 if ( element && this.elementMode != CKEDITOR.ELEMENT_MODE_APPENDTO ) {
637 var data = this.getData();
638
639 if ( this.config.htmlEncodeOutput )
640 data = CKEDITOR.tools.htmlEncode( data );
641
642 if ( element.is( 'textarea' ) )
643 element.setValue( data );
644 else
645 element.setHtml( data );
646
647 return true;
648 }
649 return false;
650 }
651
652 // Always returns ENTER_BR in case of blockless editor.
653 function validateEnterMode( editor, enterMode ) {
654 return editor.blockless ? CKEDITOR.ENTER_BR : enterMode;
655 }
656
657 // Create DocumentFragment from specified ranges. For now it handles only tables
658 // and returns DocumentFragment from the 1. range for other cases. (http://dev.ckeditor.com/ticket/13884)
659 function createDocumentFragmentFromRanges( ranges, editable ) {
660 var docFragment = new CKEDITOR.dom.documentFragment(),
661 tableClone,
662 currentRow,
663 currentRowClone;
664
665 // We must handle two cases here:
666 // 1. <tr>[<td>Cell</td>]</tr> (IE9+, Edge, Chrome, Firefox)
667 // 2. <td>[Cell]</td> (IE8-, Safari)
668 function isSelectedCell( range ) {
669 var start = range.startContainer,
670 end = range.endContainer;
671
672 if ( start.is && ( start.is( 'tr' ) ||
673 ( start.is( 'td' ) && start.equals( end ) && range.endOffset === start.getChildCount() ) ) ) {
674 return true;
675 }
676
677 return false;
678 }
679
680 function cloneCell( range ) {
681 var start = range.startContainer;
682
683 if ( start.is( 'tr' ) ) {
684 return range.cloneContents();
685 }
686
687 return start.clone( true );
688 }
689
690 for ( var i = 0; i < ranges.length; i++ ) {
691 var range = ranges[ i ],
692 container = range.startContainer.getAscendant( 'tr', true );
693
694 if ( isSelectedCell( range ) ) {
695 if ( !tableClone ) {
696 tableClone = container.getAscendant( 'table' ).clone();
697 tableClone.append( container.getAscendant( { thead: 1, tbody: 1, tfoot: 1 } ).clone() );
698 docFragment.append( tableClone );
699 tableClone = tableClone.findOne( 'thead, tbody, tfoot' );
700 }
701
702 if ( !( currentRow && currentRow.equals( container ) ) ) {
703 currentRow = container;
704 currentRowClone = container.clone();
705 tableClone.append( currentRowClone );
706 }
707
708 currentRowClone.append( cloneCell( range ) );
709 } else {
710 // If there was something else copied with table,
711 // append it to DocumentFragment.
712 docFragment.append( range.cloneContents() );
713 }
714 }
715
716 if ( !tableClone ) {
717 return editable.getHtmlFromRange( ranges[ 0 ] );
718 }
719
720 return docFragment;
721 }
722
723 CKEDITOR.tools.extend( CKEDITOR.editor.prototype, {
724 /**
725 * Adds a command definition to the editor instance. Commands added with
726 * this function can be executed later with the <code>{@link #execCommand}</code> method.
727 *
728 * editorInstance.addCommand( 'sample', {
729 * exec: function( editor ) {
730 * alert( 'Executing a command for the editor name "' + editor.name + '"!' );
731 * }
732 * } );
733 *
734 * @param {String} commandName The indentifier name of the command.
735 * @param {CKEDITOR.commandDefinition} commandDefinition The command definition.
736 */
737 addCommand: function( commandName, commandDefinition ) {
738 commandDefinition.name = commandName.toLowerCase();
739 var cmd = new CKEDITOR.command( this, commandDefinition );
740
741 // Update command when mode is set.
742 // This guarantees that commands added before first editor#mode
743 // aren't immediately updated, but waits for editor#mode and that
744 // commands added later are immediately refreshed, even when added
745 // before instanceReady. http://dev.ckeditor.com/ticket/10103, http://dev.ckeditor.com/ticket/10249
746 if ( this.mode )
747 updateCommand( this, cmd );
748
749 return this.commands[ commandName ] = cmd;
750 },
751
752 /**
753 * Attaches the editor to a form to call {@link #updateElement} before form submission.
754 * This method is called by both creators ({@link CKEDITOR#replace replace} and
755 * {@link CKEDITOR#inline inline}), so there is no reason to call it manually.
756 *
757 * @private
758 */
759 _attachToForm: function() {
760 var editor = this,
761 element = editor.element,
762 form = new CKEDITOR.dom.element( element.$.form );
763
764 // If are replacing a textarea, we must
765 if ( element.is( 'textarea' ) ) {
766 if ( form ) {
767 form.on( 'submit', onSubmit );
768
769 // Check if there is no element/elements input with name == "submit".
770 // If they exists they will overwrite form submit function (form.$.submit).
771 // If form.$.submit is overwritten we can not do anything with it.
772 if ( isFunction( form.$.submit ) ) {
773 // Setup the submit function because it doesn't fire the
774 // "submit" event.
775 form.$.submit = CKEDITOR.tools.override( form.$.submit, function( originalSubmit ) {
776 return function() {
777 onSubmit();
778
779 // For IE, the DOM submit function is not a
780 // function, so we need third check.
781 if ( originalSubmit.apply )
782 originalSubmit.apply( this );
783 else
784 originalSubmit();
785 };
786 } );
787 }
788
789 // Remove 'submit' events registered on form element before destroying.(http://dev.ckeditor.com/ticket/3988)
790 editor.on( 'destroy', function() {
791 form.removeListener( 'submit', onSubmit );
792 } );
793 }
794 }
795
796 function onSubmit( evt ) {
797 editor.updateElement();
798
799 // http://dev.ckeditor.com/ticket/8031 If textarea had required attribute and editor is empty fire 'required' event and if
800 // it was cancelled, prevent submitting the form.
801 if ( editor._.required && !element.getValue() && editor.fire( 'required' ) === false ) {
802 // When user press save button event (evt) is undefined (see save plugin).
803 // This method works because it throws error so originalSubmit won't be called.
804 // Also this error won't be shown because it will be caught in save plugin.
805 evt.data.preventDefault();
806 }
807 }
808
809 function isFunction( f ) {
810 // For IE8 typeof fun == object so we cannot use it.
811 return !!( f && f.call && f.apply );
812 }
813 },
814
815 /**
816 * Destroys the editor instance, releasing all resources used by it.
817 * If the editor replaced an element, the element will be recovered.
818 *
819 * alert( CKEDITOR.instances.editor1 ); // e.g. object
820 * CKEDITOR.instances.editor1.destroy();
821 * alert( CKEDITOR.instances.editor1 ); // undefined
822 *
823 * @param {Boolean} [noUpdate] If the instance is replacing a DOM
824 * element, this parameter indicates whether or not to update the
825 * element with the instance content.
826 */
827 destroy: function( noUpdate ) {
828 this.fire( 'beforeDestroy' );
829
830 !noUpdate && updateEditorElement.call( this );
831
832 this.editable( null );
833
834 if ( this.filter ) {
835 this.filter.destroy();
836 delete this.filter;
837 }
838
839 delete this.activeFilter;
840
841 this.status = 'destroyed';
842
843 this.fire( 'destroy' );
844
845 // Plug off all listeners to prevent any further events firing.
846 this.removeAllListeners();
847
848 CKEDITOR.remove( this );
849 CKEDITOR.fire( 'instanceDestroyed', null, this );
850 },
851
852 /**
853 * Returns an {@link CKEDITOR.dom.elementPath element path} for the selection in the editor.
854 *
855 * @param {CKEDITOR.dom.node} [startNode] From which the path should start,
856 * if not specified defaults to editor selection's
857 * start element yielded by {@link CKEDITOR.dom.selection#getStartElement}.
858 * @returns {CKEDITOR.dom.elementPath}
859 */
860 elementPath: function( startNode ) {
861 if ( !startNode ) {
862 var sel = this.getSelection();
863 if ( !sel )
864 return null;
865
866 startNode = sel.getStartElement();
867 }
868
869 return startNode ? new CKEDITOR.dom.elementPath( startNode, this.editable() ) : null;
870 },
871
872 /**
873 * Shortcut to create a {@link CKEDITOR.dom.range} instance from the editable element.
874 *
875 * @returns {CKEDITOR.dom.range} The DOM range created if the editable has presented.
876 * @see CKEDITOR.dom.range
877 */
878 createRange: function() {
879 var editable = this.editable();
880 return editable ? new CKEDITOR.dom.range( editable ) : null;
881 },
882
883 /**
884 * Executes a command associated with the editor.
885 *
886 * editorInstance.execCommand( 'bold' );
887 *
888 * @param {String} commandName The identifier name of the command.
889 * @param {Object} [data] The data to be passed to the command. It defaults to
890 * an empty object starting from 4.7.0.
891 * @returns {Boolean} `true` if the command was executed successfully, `false` otherwise.
892 * @see CKEDITOR.editor#addCommand
893 */
894 execCommand: function( commandName, data ) {
895 var command = this.getCommand( commandName );
896
897 var eventData = {
898 name: commandName,
899 commandData: data || {},
900 command: command
901 };
902
903 if ( command && command.state != CKEDITOR.TRISTATE_DISABLED ) {
904 if ( this.fire( 'beforeCommandExec', eventData ) !== false ) {
905 eventData.returnValue = command.exec( eventData.commandData );
906
907 // Fire the 'afterCommandExec' immediately if command is synchronous.
908 if ( !command.async && this.fire( 'afterCommandExec', eventData ) !== false )
909 return eventData.returnValue;
910 }
911 }
912
913 // throw 'Unknown command name "' + commandName + '"';
914 return false;
915 },
916
917 /**
918 * Gets one of the registered commands. Note that after registering a
919 * command definition with {@link #addCommand}, it is
920 * transformed internally into an instance of
921 * {@link CKEDITOR.command}, which will then be returned by this function.
922 *
923 * @param {String} commandName The name of the command to be returned.
924 * This is the same name that is used to register the command with `addCommand`.
925 * @returns {CKEDITOR.command} The command object identified by the provided name.
926 */
927 getCommand: function( commandName ) {
928 return this.commands[ commandName ];
929 },
930
931 /**
932 * Gets the editor data. The data will be in "raw" format. It is the same
933 * data that is posted by the editor.
934 *
935 * if ( CKEDITOR.instances.editor1.getData() == '' )
936 * alert( 'There is no data available.' );
937 *
938 * @param {Boolean} internal If set to `true`, it will prevent firing the
939 * {@link CKEDITOR.editor#beforeGetData} and {@link CKEDITOR.editor#event-getData} events, so
940 * the real content of the editor will not be read and cached data will be returned. The method will work
941 * much faster, but this may result in the editor returning the data that is not up to date. This parameter
942 * should thus only be set to `true` when you are certain that the cached data is up to date.
943 *
944 * @returns {String} The editor data.
945 */
946 getData: function( internal ) {
947 !internal && this.fire( 'beforeGetData' );
948
949 var eventData = this._.data;
950
951 if ( typeof eventData != 'string' ) {
952 var element = this.element;
953 if ( element && this.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE )
954 eventData = element.is( 'textarea' ) ? element.getValue() : element.getHtml();
955 else
956 eventData = '';
957 }
958
959 eventData = { dataValue: eventData };
960
961 // Fire "getData" so data manipulation may happen.
962 !internal && this.fire( 'getData', eventData );
963
964 return eventData.dataValue;
965 },
966
967 /**
968 * Gets the "raw data" currently available in the editor. This is a
969 * fast method which returns the data as is, without processing, so it is
970 * not recommended to use it on resulting pages. Instead it can be used
971 * combined with the {@link #method-loadSnapshot} method in order
972 * to automatically save the editor data from time to time
973 * while the user is using the editor, to avoid data loss, without risking
974 * performance issues.
975 *
976 * alert( editor.getSnapshot() );
977 *
978 * See also:
979 *
980 * * {@link CKEDITOR.editor#method-getData}.
981 *
982 * @returns {String} Editor "raw data".
983 */
984 getSnapshot: function() {
985 var data = this.fire( 'getSnapshot' );
986
987 if ( typeof data != 'string' ) {
988 var element = this.element;
989
990 if ( element && this.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE ) {
991 data = element.is( 'textarea' ) ? element.getValue() : element.getHtml();
992 }
993 else {
994 // If we don't have a proper element, set data to an empty string,
995 // as this method is expected to return a string. (http://dev.ckeditor.com/ticket/13385)
996 data = '';
997 }
998 }
999
1000 return data;
1001 },
1002
1003 /**
1004 * Loads "raw data" into the editor. The data is loaded with processing
1005 * straight to the editing area. It should not be used as a way to load
1006 * any kind of data, but instead in combination with
1007 * {@link #method-getSnapshot}-produced data.
1008 *
1009 * var data = editor.getSnapshot();
1010 * editor.loadSnapshot( data );
1011 *
1012 * @see CKEDITOR.editor#setData
1013 */
1014 loadSnapshot: function( snapshot ) {
1015 this.fire( 'loadSnapshot', snapshot );
1016 },
1017
1018 /**
1019 * Sets the editor data. The data must be provided in the "raw" format (HTML).
1020 *
1021 * Note that this method is asynchronous. The `callback` parameter must
1022 * be used if interaction with the editor is needed after setting the data.
1023 *
1024 * CKEDITOR.instances.editor1.setData( '<p>This is the editor data.</p>' );
1025 *
1026 * CKEDITOR.instances.editor1.setData( '<p>Some other editor data.</p>', {
1027 * callback: function() {
1028 * this.checkDirty(); // true
1029 * }
1030 * } );
1031 *
1032 * Note: In **CKEditor 4.4.2** the signature of this method has changed. All arguments
1033 * except `data` were wrapped into the `options` object. However, backward compatibility
1034 * was preserved and it is still possible to use the `data, callback, internal` arguments.
1035 *
1036 *
1037 * @param {String} data The HTML code to replace current editor content.
1038 * @param {Object} [options]
1039 * @param {Boolean} [options.internal=false] Whether to suppress any event firing when copying data internally inside the editor.
1040 * @param {Function} [options.callback] Function to be called after `setData` is completed (on {@link #dataReady}).
1041 * @param {Boolean} [options.noSnapshot=false] If set to `true`, it will prevent recording an undo snapshot.
1042 * Introduced in CKEditor 4.4.2.
1043 */
1044 setData: function( data, options, internal ) {
1045 var fireSnapshot = true,
1046 // Backward compatibility.
1047 callback = options,
1048 eventData;
1049
1050 if ( options && typeof options == 'object' ) {
1051 internal = options.internal;
1052 callback = options.callback;
1053 fireSnapshot = !options.noSnapshot;
1054 }
1055
1056 if ( !internal && fireSnapshot )
1057 this.fire( 'saveSnapshot' );
1058
1059 if ( callback || !internal ) {
1060 this.once( 'dataReady', function( evt ) {
1061 if ( !internal && fireSnapshot )
1062 this.fire( 'saveSnapshot' );
1063
1064 if ( callback )
1065 callback.call( evt.editor );
1066 } );
1067 }
1068
1069 // Fire "setData" so data manipulation may happen.
1070 eventData = { dataValue: data };
1071 !internal && this.fire( 'setData', eventData );
1072
1073 this._.data = eventData.dataValue;
1074
1075 !internal && this.fire( 'afterSetData', eventData );
1076 },
1077
1078 /**
1079 * Puts or restores the editor into the read-only state. When in read-only,
1080 * the user is not able to change the editor content, but can still use
1081 * some editor features. This function sets the {@link #property-readOnly}
1082 * property of the editor, firing the {@link #event-readOnly} event.
1083 *
1084 * **Note:** The current editing area will be reloaded.
1085 *
1086 * @since 3.6
1087 * @param {Boolean} [isReadOnly] Indicates that the editor must go
1088 * read-only (`true`, default) or be restored and made editable (`false`).
1089 */
1090 setReadOnly: function( isReadOnly ) {
1091 isReadOnly = ( isReadOnly == null ) || isReadOnly;
1092
1093 if ( this.readOnly != isReadOnly ) {
1094 this.readOnly = isReadOnly;
1095
1096 // Block or release BACKSPACE key according to current read-only
1097 // state to prevent browser's history navigation (http://dev.ckeditor.com/ticket/9761).
1098 this.keystrokeHandler.blockedKeystrokes[ 8 ] = +isReadOnly;
1099
1100 this.editable().setReadOnly( isReadOnly );
1101
1102 // Fire the readOnly event so the editor features can update
1103 // their state accordingly.
1104 this.fire( 'readOnly' );
1105 }
1106 },
1107
1108 /**
1109 * Inserts HTML code into the currently selected position in the editor in WYSIWYG mode.
1110 *
1111 * Example:
1112 *
1113 * CKEDITOR.instances.editor1.insertHtml( '<p>This is a new paragraph.</p>' );
1114 *
1115 * Fires the {@link #event-insertHtml} and {@link #event-afterInsertHtml} events. The HTML is inserted
1116 * in the {@link #event-insertHtml} event's listener with a default priority (10) so you can add listeners with
1117 * lower or higher priorities in order to execute some code before or after the HTML is inserted.
1118 *
1119 * @param {String} html HTML code to be inserted into the editor.
1120 * @param {String} [mode='html'] The mode in which the HTML code will be inserted. One of the following:
1121 *
1122 * * `'html'` &ndash; The inserted content will completely override the styles at the selected position.
1123 * * `'unfiltered_html'` &ndash; Like `'html'` but the content is not filtered with {@link CKEDITOR.filter}.
1124 * * `'text'` &ndash; The inserted content will inherit the styles applied in
1125 * the selected position. This mode should be used when inserting "htmlified" plain text
1126 * (HTML without inline styles and styling elements like `<b>`, `<strong>`, `<span style="...">`).
1127 *
1128 * @param {CKEDITOR.dom.range} [range] If specified, the HTML will be inserted into the range
1129 * instead of into the selection. The selection will be placed at the end of the insertion (like in the normal case).
1130 * Introduced in CKEditor 4.5.
1131 */
1132 insertHtml: function( html, mode, range ) {
1133 this.fire( 'insertHtml', { dataValue: html, mode: mode, range: range } );
1134 },
1135
1136 /**
1137 * Inserts text content into the currently selected position in the
1138 * editor in WYSIWYG mode. The styles of the selected element will be applied to the inserted text.
1139 * Spaces around the text will be left untouched.
1140 *
1141 * CKEDITOR.instances.editor1.insertText( ' line1 \n\n line2' );
1142 *
1143 * Fires the {@link #event-insertText} and {@link #event-afterInsertHtml} events. The text is inserted
1144 * in the {@link #event-insertText} event's listener with a default priority (10) so you can add listeners with
1145 * lower or higher priorities in order to execute some code before or after the text is inserted.
1146 *
1147 * @since 3.5
1148 * @param {String} text Text to be inserted into the editor.
1149 */
1150 insertText: function( text ) {
1151 this.fire( 'insertText', text );
1152 },
1153
1154 /**
1155 * Inserts an element into the currently selected position in the editor in WYSIWYG mode.
1156 *
1157 * var element = CKEDITOR.dom.element.createFromHtml( '<img src="hello.png" border="0" title="Hello" />' );
1158 * CKEDITOR.instances.editor1.insertElement( element );
1159 *
1160 * Fires the {@link #event-insertElement} event. The element is inserted in the listener with a default priority (10),
1161 * so you can add listeners with lower or higher priorities in order to execute some code before or after
1162 * the element is inserted.
1163 *
1164 * @param {CKEDITOR.dom.element} element The element to be inserted into the editor.
1165 */
1166 insertElement: function( element ) {
1167 this.fire( 'insertElement', element );
1168 },
1169
1170 /**
1171 * Gets the selected HTML (it is returned as a {@link CKEDITOR.dom.documentFragment document fragment}
1172 * or a string). This method is designed to work as the user would expect the copy functionality to work.
1173 * For instance, if the following selection was made:
1174 *
1175 * <p>a<b>b{c}d</b>e</p>
1176 *
1177 * The following HTML will be returned:
1178 *
1179 * <b>c</b>
1180 *
1181 * As you can see, the information about the bold formatting was preserved, even though the selection was
1182 * anchored inside the `<b>` element.
1183 *
1184 * See also:
1185 *
1186 * * the {@link #extractSelectedHtml} method,
1187 * * the {@link CKEDITOR.editable#getHtmlFromRange} method.
1188 *
1189 * @since 4.5
1190 * @param {Boolean} [toString] If `true`, then stringified HTML will be returned.
1191 * @returns {CKEDITOR.dom.documentFragment/String}
1192 */
1193 getSelectedHtml: function( toString ) {
1194 var editable = this.editable(),
1195 selection = this.getSelection(),
1196 ranges = selection && selection.getRanges();
1197
1198 if ( !editable || !ranges || ranges.length === 0 ) {
1199 return null;
1200 }
1201
1202 var docFragment = createDocumentFragmentFromRanges( ranges, editable );
1203
1204 return toString ? docFragment.getHtml() : docFragment;
1205 },
1206
1207 /**
1208 * Gets the selected HTML (it is returned as a {@link CKEDITOR.dom.documentFragment document fragment}
1209 * or a string) and removes the selected part of the DOM. This method is designed to work as the user would
1210 * expect the cut and delete functionalities to work.
1211 *
1212 * See also:
1213 *
1214 * * the {@link #getSelectedHtml} method,
1215 * * the {@link CKEDITOR.editable#extractHtmlFromRange} method.
1216 *
1217 * @since 4.5
1218 * @param {Boolean} [toString] If `true`, then stringified HTML will be returned.
1219 * @param {Boolean} [removeEmptyBlock=false] Default `false` means that the function will keep an empty block (if the
1220 * entire content was removed) or it will create it (if a block element was removed) and set the selection in that block.
1221 * If `true`, the empty block will be removed or not created. In this case the function will not handle the selection.
1222 * @returns {CKEDITOR.dom.documentFragment/String/null}
1223 */
1224 extractSelectedHtml: function( toString, removeEmptyBlock ) {
1225 var editable = this.editable(),
1226 ranges = this.getSelection().getRanges(),
1227 docFragment = new CKEDITOR.dom.documentFragment(),
1228 i;
1229
1230 if ( !editable || ranges.length === 0 ) {
1231 return null;
1232 }
1233
1234 for ( i = 0; i < ranges.length; i++ ) {
1235 docFragment.append( editable.extractHtmlFromRange( ranges[ i ], removeEmptyBlock ) );
1236 }
1237
1238 if ( !removeEmptyBlock ) {
1239 this.getSelection().selectRanges( [ ranges[ 0 ] ] );
1240 }
1241
1242 return toString ? docFragment.getHtml() : docFragment;
1243 },
1244
1245 /**
1246 * Moves the selection focus to the editing area space in the editor.
1247 */
1248 focus: function() {
1249 this.fire( 'beforeFocus' );
1250 },
1251
1252 /**
1253 * Checks whether the current editor content contains changes when
1254 * compared to the content loaded into the editor at startup, or to
1255 * the content available in the editor when {@link #resetDirty}
1256 * was called.
1257 *
1258 * function beforeUnload( evt ) {
1259 * if ( CKEDITOR.instances.editor1.checkDirty() )
1260 * return evt.returnValue = "You will lose the changes made in the editor.";
1261 * }
1262 *
1263 * if ( window.addEventListener )
1264 * window.addEventListener( 'beforeunload', beforeUnload, false );
1265 * else
1266 * window.attachEvent( 'onbeforeunload', beforeUnload );
1267 *
1268 * @returns {Boolean} `true` if the content contains changes.
1269 */
1270 checkDirty: function() {
1271 return this.status == 'ready' && this._.previousValue !== this.getSnapshot();
1272 },
1273
1274 /**
1275 * Resets the "dirty state" of the editor so subsequent calls to
1276 * {@link #checkDirty} will return `false` if the user will not
1277 * have made further changes to the content.
1278 *
1279 * alert( editor.checkDirty() ); // e.g. true
1280 * editor.resetDirty();
1281 * alert( editor.checkDirty() ); // false
1282 */
1283 resetDirty: function() {
1284 this._.previousValue = this.getSnapshot();
1285 },
1286
1287 /**
1288 * Updates the `<textarea>` element that was replaced by the editor with
1289 * the current data available in the editor.
1290 *
1291 * **Note:** This method will only affect those editor instances created
1292 * with the {@link CKEDITOR#ELEMENT_MODE_REPLACE} element mode or inline instances
1293 * bound to `<textarea>` elements.
1294 *
1295 * CKEDITOR.instances.editor1.updateElement();
1296 * alert( document.getElementById( 'editor1' ).value ); // The current editor data.
1297 *
1298 * @see CKEDITOR.editor#element
1299 */
1300 updateElement: function() {
1301 return updateEditorElement.call( this );
1302 },
1303
1304 /**
1305 * Assigns keystrokes associated with editor commands.
1306 *
1307 * editor.setKeystroke( CKEDITOR.CTRL + 115, 'save' ); // Assigned Ctrl+S to the "save" command.
1308 * editor.setKeystroke( CKEDITOR.CTRL + 115, false ); // Disabled Ctrl+S keystroke assignment.
1309 * editor.setKeystroke( [
1310 * [ CKEDITOR.ALT + 122, false ],
1311 * [ CKEDITOR.CTRL + 121, 'link' ],
1312 * [ CKEDITOR.SHIFT + 120, 'bold' ]
1313 * ] );
1314 *
1315 * This method may be used in the following cases:
1316 *
1317 * * By plugins (like `link` or `basicstyles`) to set their keystrokes when plugins are being loaded.
1318 * * During the runtime to modify existing keystrokes.
1319 *
1320 * The editor handles keystroke configuration in the following order:
1321 *
1322 * 1. Plugins use this method to define default keystrokes.
1323 * 2. Editor extends default keystrokes with {@link CKEDITOR.config#keystrokes}.
1324 * 3. Editor blocks keystrokes defined in {@link CKEDITOR.config#blockedKeystrokes}.
1325 *
1326 * You can then set new keystrokes using this method during the runtime.
1327 *
1328 * @since 4.0
1329 * @param {Number/Array} keystroke A keystroke or an array of keystroke definitions.
1330 * @param {String/Boolean} [behavior] A command to be executed on the keystroke.
1331 */
1332 setKeystroke: function() {
1333 var keystrokes = this.keystrokeHandler.keystrokes,
1334 newKeystrokes = CKEDITOR.tools.isArray( arguments[ 0 ] ) ? arguments[ 0 ] : [ [].slice.call( arguments, 0 ) ],
1335 keystroke, behavior;
1336
1337 for ( var i = newKeystrokes.length; i--; ) {
1338 keystroke = newKeystrokes[ i ];
1339 behavior = 0;
1340
1341 // It may be a pair of: [ key, command ]
1342 if ( CKEDITOR.tools.isArray( keystroke ) ) {
1343 behavior = keystroke[ 1 ];
1344 keystroke = keystroke[ 0 ];
1345 }
1346
1347 if ( behavior )
1348 keystrokes[ keystroke ] = behavior;
1349 else
1350 delete keystrokes[ keystroke ];
1351 }
1352 },
1353
1354 /**
1355 * Returns the keystroke that is assigned to a specified {@link CKEDITOR.command}. If no keystroke is assigned,
1356 * it returns `null`.
1357 *
1358 * Since version 4.7.0 this function also accepts a `command` parameter as a string.
1359 *
1360 * @since 4.6.0
1361 * @param {CKEDITOR.command/String} command The {@link CKEDITOR.command} instance or a string with the command name.
1362 * @returns {Number/null} The keystroke assigned to the provided command or `null` if there is no keystroke.
1363 */
1364 getCommandKeystroke: function( command ) {
1365 var commandInstance = ( typeof command === 'string' ? this.getCommand( command ) : command );
1366
1367 if ( commandInstance ) {
1368 var commandName = CKEDITOR.tools.object.findKey( this.commands, commandInstance ),
1369 keystrokes = this.keystrokeHandler.keystrokes,
1370 key;
1371
1372 // Some commands have a fake keystroke - for example CUT/COPY/PASTE commands are handled natively.
1373 if ( commandInstance.fakeKeystroke ) {
1374 return commandInstance.fakeKeystroke;
1375 }
1376
1377 for ( key in keystrokes ) {
1378 if ( keystrokes.hasOwnProperty( key ) && keystrokes[ key ] == commandName ) {
1379 return key;
1380 }
1381 }
1382 }
1383 return null;
1384 },
1385
1386 /**
1387 * Shorthand for {@link CKEDITOR.filter#addFeature}.
1388 *
1389 * @since 4.1
1390 * @param {CKEDITOR.feature} feature See {@link CKEDITOR.filter#addFeature}.
1391 * @returns {Boolean} See {@link CKEDITOR.filter#addFeature}.
1392 */
1393 addFeature: function( feature ) {
1394 return this.filter.addFeature( feature );
1395 },
1396
1397 /**
1398 * Sets the active filter ({@link #activeFilter}). Fires the {@link #activeFilterChange} event.
1399 *
1400 * // Set active filter which allows only 4 elements.
1401 * // Buttons like Bold, Italic will be disabled.
1402 * var filter = new CKEDITOR.filter( 'p strong em br' );
1403 * editor.setActiveFilter( filter );
1404 *
1405 * Setting a new filter will also change the {@link #setActiveEnterMode active Enter modes} to the first values
1406 * allowed by the new filter (see {@link CKEDITOR.filter#getAllowedEnterMode}).
1407 *
1408 * @since 4.3
1409 * @param {CKEDITOR.filter} filter Filter instance or a falsy value (e.g. `null`) to reset to the default one.
1410 */
1411 setActiveFilter: function( filter ) {
1412 if ( !filter )
1413 filter = this.filter;
1414
1415 if ( this.activeFilter !== filter ) {
1416 this.activeFilter = filter;
1417 this.fire( 'activeFilterChange' );
1418
1419 // Reset active filter to the main one - it resets enter modes, too.
1420 if ( filter === this.filter )
1421 this.setActiveEnterMode( null, null );
1422 else
1423 this.setActiveEnterMode(
1424 filter.getAllowedEnterMode( this.enterMode ),
1425 filter.getAllowedEnterMode( this.shiftEnterMode, true )
1426 );
1427 }
1428 },
1429
1430 /**
1431 * Sets the active Enter modes: ({@link #enterMode} and {@link #shiftEnterMode}).
1432 * Fires the {@link #activeEnterModeChange} event.
1433 *
1434 * Prior to CKEditor 4.3 Enter modes were static and it was enough to check {@link CKEDITOR.config#enterMode}
1435 * and {@link CKEDITOR.config#shiftEnterMode} when implementing a feature which should depend on the Enter modes.
1436 * Since CKEditor 4.3 these options are source of initial:
1437 *
1438 * * static {@link #enterMode} and {@link #shiftEnterMode} values,
1439 * * dynamic {@link #activeEnterMode} and {@link #activeShiftEnterMode} values.
1440 *
1441 * However, the dynamic Enter modes can be changed during runtime by using this method, to reflect the selection context.
1442 * For example, if selection is moved to the {@link CKEDITOR.plugins.widget widget}'s nested editable which
1443 * is a {@link #blockless blockless one}, then the active Enter modes should be changed to {@link CKEDITOR#ENTER_BR}
1444 * (in this case [Widget System](#!/guide/dev_widgets) takes care of that).
1445 *
1446 * **Note:** This method should not be used to configure the editor &ndash; use {@link CKEDITOR.config#enterMode} and
1447 * {@link CKEDITOR.config#shiftEnterMode} instead. This method should only be used to dynamically change
1448 * Enter modes during runtime based on selection changes.
1449 * Keep in mind that changed Enter mode may be overwritten by another plugin/feature when it decided that
1450 * the changed context requires this.
1451 *
1452 * **Note:** In case of blockless editor (inline editor based on an element which cannot contain block elements
1453 * &mdash; see {@link CKEDITOR.editor#blockless}) only {@link CKEDITOR#ENTER_BR} is a valid Enter mode. Therefore
1454 * this method will not allow to set other values.
1455 *
1456 * **Note:** Changing the {@link #activeFilter active filter} may cause the Enter mode to change if default Enter modes
1457 * are not allowed by the new filter.
1458 *
1459 * @since 4.3
1460 * @param {Number} enterMode One of {@link CKEDITOR#ENTER_P}, {@link CKEDITOR#ENTER_DIV}, {@link CKEDITOR#ENTER_BR}.
1461 * Pass falsy value (e.g. `null`) to reset the Enter mode to the default value ({@link #enterMode} and/or {@link #shiftEnterMode}).
1462 * @param {Number} shiftEnterMode See the `enterMode` argument.
1463 */
1464 setActiveEnterMode: function( enterMode, shiftEnterMode ) {
1465 // Validate passed modes or use default ones (validated on init).
1466 enterMode = enterMode ? validateEnterMode( this, enterMode ) : this.enterMode;
1467 shiftEnterMode = shiftEnterMode ? validateEnterMode( this, shiftEnterMode ) : this.shiftEnterMode;
1468
1469 if ( this.activeEnterMode != enterMode || this.activeShiftEnterMode != shiftEnterMode ) {
1470 this.activeEnterMode = enterMode;
1471 this.activeShiftEnterMode = shiftEnterMode;
1472 this.fire( 'activeEnterModeChange' );
1473 }
1474 },
1475
1476 /**
1477 * Shows a notification to the user.
1478 *
1479 * If the [Notification](http://ckeditor.com/addons/notification) plugin is not enabled, this function shows
1480 * a normal alert with the given `message`. The `type` and `progressOrDuration` parameters are supported
1481 * only by the Notification plugin.
1482 *
1483 * If the Notification plugin is enabled, this method creates and shows a new notification.
1484 * By default the notification is shown over the editor content, in the viewport if it is possible.
1485 *
1486 * See {@link CKEDITOR.plugins.notification}.
1487 *
1488 * @since 4.5
1489 * @member CKEDITOR.editor
1490 * @param {String} message The message displayed in the notification.
1491 * @param {String} [type='info'] The type of the notification. Can be `'info'`, `'warning'`, `'success'` or `'progress'`.
1492 * @param {Number} [progressOrDuration] If the type is `progress`, the third parameter may be a progress from `0` to `1`
1493 * (defaults to `0`). Otherwise the third parameter may be a notification duration denoting after how many milliseconds
1494 * the notification should be closed automatically. `0` means that the notification will not close automatically and the user
1495 * needs to close it manually. See {@link CKEDITOR.plugins.notification#duration}.
1496 * Note that `warning` notifications will not be closed automatically.
1497 * @returns {CKEDITOR.plugins.notification} Created and shown notification.
1498 */
1499 showNotification: function( message ) {
1500 alert( message ); // jshint ignore:line
1501 }
1502 } );
1503 } )();
1504
1505 /**
1506 * The editor has no associated element.
1507 *
1508 * @readonly
1509 * @property {Number} [=0]
1510 * @member CKEDITOR
1511 */
1512 CKEDITOR.ELEMENT_MODE_NONE = 0;
1513
1514 /**
1515 * The element is to be replaced by the editor instance.
1516 *
1517 * @readonly
1518 * @property {Number} [=1]
1519 * @member CKEDITOR
1520 */
1521 CKEDITOR.ELEMENT_MODE_REPLACE = 1;
1522
1523 /**
1524 * The editor is to be created inside the element.
1525 *
1526 * @readonly
1527 * @property {Number} [=2]
1528 * @member CKEDITOR
1529 */
1530 CKEDITOR.ELEMENT_MODE_APPENDTO = 2;
1531
1532 /**
1533 * The editor is to be attached to the element, using it as the editing block.
1534 *
1535 * @readonly
1536 * @property {Number} [=3]
1537 * @member CKEDITOR
1538 */
1539 CKEDITOR.ELEMENT_MODE_INLINE = 3;
1540
1541 /**
1542 * Whether to escape HTML when the editor updates the original input element.
1543 *
1544 * config.htmlEncodeOutput = true;
1545 *
1546 * @since 3.1
1547 * @cfg {Boolean} [htmlEncodeOutput=false]
1548 * @member CKEDITOR.config
1549 */
1550
1551 /**
1552 * If `true`, makes the editor start in read-only state. Otherwise, it will check
1553 * if the linked `<textarea>` element has the `disabled` attribute.
1554 *
1555 * Read more in the [documentation](#!/guide/dev_readonly)
1556 * and see the [SDK sample](http://sdk.ckeditor.com/samples/readonly.html).
1557 *
1558 * config.readOnly = true;
1559 *
1560 * @since 3.6
1561 * @cfg {Boolean} [readOnly=false]
1562 * @member CKEDITOR.config
1563 * @see CKEDITOR.editor#setReadOnly
1564 */
1565
1566 /**
1567 * Whether an editable element should have focus when the editor is loading for the first time.
1568 *
1569 * config.startupFocus = true;
1570 *
1571 * @cfg {Boolean} [startupFocus=false]
1572 * @member CKEDITOR.config
1573 */
1574
1575 /**
1576 * Customizes the {@link CKEDITOR.editor#title human-readable title} of this editor. This title is displayed in
1577 * tooltips and impacts various [accessibility aspects](#!/guide/dev_a11y-section-announcing-the-editor-on-the-page),
1578 * e.g. it is commonly used by screen readers for distinguishing editor instances and for navigation.
1579 * Accepted values are a string or `false`.
1580 *
1581 * **Note:** When `config.title` is set globally, the same value will be applied to all editor instances
1582 * loaded with this config. This may adversely affect accessibility as screen reader users will be unable
1583 * to distinguish particular editor instances and navigate between them.
1584 *
1585 * **Note:** Setting `config.title = false` may also impair accessibility in a similar way.
1586 *
1587 * **Note:** Please do not confuse this property with {@link CKEDITOR.editor#name}
1588 * which identifies the instance in the {@link CKEDITOR#instances} literal.
1589 *
1590 * // Sets the title to 'My WYSIWYG editor.'. The original title of the element (if it exists)
1591 * // will be restored once the editor instance is destroyed.
1592 * config.title = 'My WYSIWYG editor.';
1593 *
1594 * // Do not touch the title. If the element already has a title, it remains unchanged.
1595 * // Also if no `title` attribute exists, nothing new will be added.
1596 * config.title = false;
1597 *
1598 * See also:
1599 *
1600 * * CKEDITOR.editor#name
1601 * * CKEDITOR.editor#title
1602 *
1603 * @since 4.2
1604 * @cfg {String/Boolean} [title=based on editor.name]
1605 * @member CKEDITOR.config
1606 */
1607
1608 /**
1609 * Sets listeners on editor events.
1610 *
1611 * **Note:** This property can only be set in the `config` object passed directly
1612 * to {@link CKEDITOR#replace}, {@link CKEDITOR#inline}, and other creators.
1613 *
1614 * CKEDITOR.replace( 'editor1', {
1615 * on: {
1616 * instanceReady: function() {
1617 * alert( this.name ); // 'editor1'
1618 * },
1619 *
1620 * key: function() {
1621 * // ...
1622 * }
1623 * }
1624 * } );
1625 *
1626 * @cfg {Object} on
1627 * @member CKEDITOR.config
1628 */
1629
1630 /**
1631 * The outermost element in the DOM tree in which the editable element resides. It is provided
1632 * by a specific editor creator after the editor UI is created and is not intended to
1633 * be modified.
1634 *
1635 * var editor = CKEDITOR.instances.editor1;
1636 * alert( editor.container.getName() ); // 'span'
1637 *
1638 * @readonly
1639 * @property {CKEDITOR.dom.element} container
1640 */
1641
1642 /**
1643 * The document that stores the editor content.
1644 *
1645 * * For the classic (`iframe`-based) editor it is equal to the document inside the
1646 * `iframe` containing the editable element.
1647 * * For the inline editor it is equal to {@link CKEDITOR#document}.
1648 *
1649 * The document object is available after the {@link #contentDom} event is fired
1650 * and may be invalidated when the {@link #contentDomUnload} event is fired
1651 * (classic editor only).
1652 *
1653 * editor.on( 'contentDom', function() {
1654 * console.log( editor.document );
1655 * } );
1656 *
1657 * @readonly
1658 * @property {CKEDITOR.dom.document} document
1659 */
1660
1661 /**
1662 * The window instance related to the {@link #document} property.
1663 *
1664 * It is always equal to the `editor.document.getWindow()`.
1665 *
1666 * See the {@link #document} property documentation.
1667 *
1668 * @readonly
1669 * @property {CKEDITOR.dom.window} window
1670 */
1671
1672 /**
1673 * The main filter instance used for input data filtering, data
1674 * transformations, and activation of features.
1675 *
1676 * It points to a {@link CKEDITOR.filter} instance set up based on
1677 * editor configuration.
1678 *
1679 * @since 4.1
1680 * @readonly
1681 * @property {CKEDITOR.filter} filter
1682 */
1683
1684 /**
1685 * The active filter instance which should be used in the current context (location selection).
1686 * This instance will be used to make a decision which commands, buttons and other
1687 * {@link CKEDITOR.feature features} can be enabled.
1688 *
1689 * By default it equals the {@link #filter} and it can be changed by the {@link #setActiveFilter} method.
1690 *
1691 * editor.on( 'activeFilterChange', function() {
1692 * if ( editor.activeFilter.check( 'cite' ) )
1693 * // Do something when <cite> was enabled - e.g. enable a button.
1694 * else
1695 * // Otherwise do something else.
1696 * } );
1697 *
1698 * See also the {@link #setActiveEnterMode} method for an explanation of dynamic settings.
1699 *
1700 * @since 4.3
1701 * @readonly
1702 * @property {CKEDITOR.filter} activeFilter
1703 */
1704
1705 /**
1706 * The main (static) Enter mode which is a validated version of the {@link CKEDITOR.config#enterMode} setting.
1707 * Currently only one rule exists &mdash; {@link #blockless blockless editors} may have
1708 * Enter modes set only to {@link CKEDITOR#ENTER_BR}.
1709 *
1710 * @since 4.3
1711 * @readonly
1712 * @property {Number} enterMode
1713 */
1714
1715 /**
1716 * See the {@link #enterMode} property.
1717 *
1718 * @since 4.3
1719 * @readonly
1720 * @property {Number} shiftEnterMode
1721 */
1722
1723 /**
1724 * The dynamic Enter mode which should be used in the current context (selection location).
1725 * By default it equals the {@link #enterMode} and it can be changed by the {@link #setActiveEnterMode} method.
1726 *
1727 * See also the {@link #setActiveEnterMode} method for an explanation of dynamic settings.
1728 *
1729 * @since 4.3
1730 * @readonly
1731 * @property {Number} activeEnterMode
1732 */
1733
1734 /**
1735 * See the {@link #activeEnterMode} property.
1736 *
1737 * @since 4.3
1738 * @readonly
1739 * @property {Number} activeShiftEnterMode
1740 */
1741
1742 /**
1743 * Event fired by the {@link #setActiveFilter} method when the {@link #activeFilter} is changed.
1744 *
1745 * @since 4.3
1746 * @event activeFilterChange
1747 */
1748
1749 /**
1750 * Event fired by the {@link #setActiveEnterMode} method when any of the active Enter modes is changed.
1751 * See also the {@link #activeEnterMode} and {@link #activeShiftEnterMode} properties.
1752 *
1753 * @since 4.3
1754 * @event activeEnterModeChange
1755 */
1756
1757 /**
1758 * Event fired when a CKEDITOR instance is created, but still before initializing it.
1759 * To interact with a fully initialized instance, use the
1760 * {@link CKEDITOR#instanceReady} event instead.
1761 *
1762 * @event instanceCreated
1763 * @member CKEDITOR
1764 * @param {CKEDITOR.editor} editor The editor instance that has been created.
1765 */
1766
1767 /**
1768 * Event fired when CKEDITOR instance's components (configuration, languages and plugins) are fully
1769 * loaded and initialized. However, the editor will be fully ready for interaction
1770 * on {@link CKEDITOR#instanceReady}.
1771 *
1772 * @event instanceLoaded
1773 * @member CKEDITOR
1774 * @param {CKEDITOR.editor} editor This editor instance that has been loaded.
1775 */
1776
1777 /**
1778 * Event fired when a CKEDITOR instance is destroyed.
1779 *
1780 * @event instanceDestroyed
1781 * @member CKEDITOR
1782 * @param {CKEDITOR.editor} editor The editor instance that has been destroyed.
1783 */
1784
1785 /**
1786 * Event fired when a CKEDITOR instance is created, fully initialized and ready for interaction.
1787 *
1788 * @event instanceReady
1789 * @member CKEDITOR
1790 * @param {CKEDITOR.editor} editor The editor instance that has been created.
1791 */
1792
1793 /**
1794 * Event fired when the language is loaded into the editor instance.
1795 *
1796 * @since 3.6.1
1797 * @event langLoaded
1798 * @param {CKEDITOR.editor} editor This editor instance.
1799 */
1800
1801 /**
1802 * Event fired when all plugins are loaded and initialized into the editor instance.
1803 *
1804 * @event pluginsLoaded
1805 * @param {CKEDITOR.editor} editor This editor instance.
1806 */
1807
1808 /**
1809 * Event fired when the styles set is loaded. During the editor initialization
1810 * phase the {@link #getStylesSet} method returns only styles that
1811 * are already loaded, which may not include e.g. styles parsed
1812 * by the `stylesheetparser` plugin. Thus, to be notified when all
1813 * styles are ready, you can listen on this event.
1814 *
1815 * @since 4.1
1816 * @event stylesSet
1817 * @param {CKEDITOR.editor} editor This editor instance.
1818 * @param {Array} styles An array of styles definitions.
1819 */
1820
1821 /**
1822 * Event fired before the command execution when {@link #execCommand} is called.
1823 *
1824 * @event beforeCommandExec
1825 * @param {CKEDITOR.editor} editor This editor instance.
1826 * @param data
1827 * @param {String} data.name The command name.
1828 * @param {Object} data.commandData The data to be sent to the command. This
1829 * can be manipulated by the event listener.
1830 * @param {CKEDITOR.command} data.command The command itself.
1831 */
1832
1833 /**
1834 * Event fired after the command execution when {@link #execCommand} is called.
1835 *
1836 * @event afterCommandExec
1837 * @param {CKEDITOR.editor} editor This editor instance.
1838 * @param data
1839 * @param {String} data.name The command name.
1840 * @param {Object} data.commandData The data sent to the command.
1841 * @param {CKEDITOR.command} data.command The command itself.
1842 * @param {Object} data.returnValue The value returned by the command execution.
1843 */
1844
1845 /**
1846 * Event fired when a custom configuration file is loaded, before the final
1847 * configuration initialization.
1848 *
1849 * Custom configuration files can be loaded thorugh the
1850 * {@link CKEDITOR.config#customConfig} setting. Several files can be loaded
1851 * by changing this setting.
1852 *
1853 * @event customConfigLoaded
1854 * @param {CKEDITOR.editor} editor This editor instance.
1855 */
1856
1857 /**
1858 * Event fired once the editor configuration is ready (loaded and processed).
1859 *
1860 * @event configLoaded
1861 * @param {CKEDITOR.editor} editor This editor instance.
1862 */
1863
1864 /**
1865 * Event fired when this editor instance is destroyed. The editor at this
1866 * point is not usable and this event should be used to perform the clean-up
1867 * in any plugin.
1868 *
1869 * @event destroy
1870 * @param {CKEDITOR.editor} editor This editor instance.
1871 */
1872
1873 /**
1874 * Event fired when the {@link #method-destroy} method is called,
1875 * but before destroying the editor.
1876 *
1877 * @event beforeDestroy
1878 * @param {CKEDITOR.editor} editor This editor instance.
1879 */
1880
1881 /**
1882 * Internal event to get the current data.
1883 *
1884 * @event beforeGetData
1885 * @param {CKEDITOR.editor} editor This editor instance.
1886 */
1887
1888 /**
1889 * Internal event to perform the {@link #method-getSnapshot} call.
1890 *
1891 * @event getSnapshot
1892 * @param {CKEDITOR.editor} editor This editor instance.
1893 */
1894
1895 /**
1896 * Internal event to perform the {@link #method-loadSnapshot} call.
1897 *
1898 * @event loadSnapshot
1899 * @param {CKEDITOR.editor} editor This editor instance.
1900 * @param {String} data The data that will be used.
1901 */
1902
1903 /**
1904 * Event fired before the {@link #method-getData} call returns, allowing for additional manipulation.
1905 *
1906 * @event getData
1907 * @param {CKEDITOR.editor} editor This editor instance.
1908 * @param data
1909 * @param {String} data.dataValue The data that will be returned.
1910 */
1911
1912 /**
1913 * Event fired before the {@link #method-setData} call is executed, allowing for additional manipulation.
1914 *
1915 * @event setData
1916 * @param {CKEDITOR.editor} editor This editor instance.
1917 * @param data
1918 * @param {String} data.dataValue The data that will be used.
1919 */
1920
1921 /**
1922 * Event fired at the end of the {@link #method-setData} call execution. Usually it is better to use the
1923 * {@link #dataReady} event.
1924 *
1925 * @event afterSetData
1926 * @param {CKEDITOR.editor} editor This editor instance.
1927 * @param data
1928 * @param {String} data.dataValue The data that has been set.
1929 */
1930
1931 /**
1932 * Event fired as an indicator of the editor data loading. It may be the result of
1933 * calling {@link #method-setData} explicitly or an internal
1934 * editor function, like the editor editing mode switching (move to Source and back).
1935 *
1936 * @event dataReady
1937 * @param {CKEDITOR.editor} editor This editor instance.
1938 */
1939
1940 /**
1941 * Event fired when the CKEDITOR instance is completely created, fully initialized
1942 * and ready for interaction.
1943 *
1944 * @event instanceReady
1945 * @param {CKEDITOR.editor} editor This editor instance.
1946 */
1947
1948 /**
1949 * Event fired when editor components (configuration, languages and plugins) are fully
1950 * loaded and initialized. However, the editor will be fully ready to for interaction
1951 * on {@link #instanceReady}.
1952 *
1953 * @event loaded
1954 * @param {CKEDITOR.editor} editor This editor instance.
1955 */
1956
1957 /**
1958 * Event fired by the {@link #method-insertHtml} method. See the method documentation for more information
1959 * about how this event can be used.
1960 *
1961 * @event insertHtml
1962 * @param {CKEDITOR.editor} editor This editor instance.
1963 * @param data
1964 * @param {String} data.mode The mode in which the data is inserted (see {@link #method-insertHtml}).
1965 * @param {String} data.dataValue The HTML code to insert.
1966 * @param {CKEDITOR.dom.range} [data.range] See {@link #method-insertHtml}'s `range` parameter.
1967 */
1968
1969 /**
1970 * Event fired by the {@link #method-insertText} method. See the method documentation for more information
1971 * about how this event can be used.
1972 *
1973 * @event insertText
1974 * @param {CKEDITOR.editor} editor This editor instance.
1975 * @param {String} data The text to insert.
1976 */
1977
1978 /**
1979 * Event fired by the {@link #method-insertElement} method. See the method documentation for more information
1980 * about how this event can be used.
1981 *
1982 * @event insertElement
1983 * @param {CKEDITOR.editor} editor This editor instance.
1984 * @param {CKEDITOR.dom.element} data The element to insert.
1985 */
1986
1987 /**
1988 * Event fired after data insertion using the {@link #method-insertHtml}, {@link CKEDITOR.editable#insertHtml},
1989 * or {@link CKEDITOR.editable#insertHtmlIntoRange} methods.
1990 *
1991 * @since 4.5
1992 * @event afterInsertHtml
1993 * @param data
1994 * @param {CKEDITOR.dom.range} [data.intoRange] If set, the HTML was not inserted into the current selection, but into
1995 * the specified range. This property is set if the {@link CKEDITOR.editable#insertHtmlIntoRange} method was used,
1996 * but not if for the {@link CKEDITOR.editable#insertHtml} method.
1997 */
1998
1999 /**
2000 * Event fired after the {@link #property-readOnly} property changes.
2001 *
2002 * @since 3.6
2003 * @event readOnly
2004 * @param {CKEDITOR.editor} editor This editor instance.
2005 */
2006
2007 /**
2008 * Event fired when a UI template is added to the editor instance. It makes
2009 * it possible to bring customizations to the template source.
2010 *
2011 * @event template
2012 * @param {CKEDITOR.editor} editor This editor instance.
2013 * @param data
2014 * @param {String} data.name The template name.
2015 * @param {String} data.source The source data for this template.
2016 */
2017
2018 /**
2019 * Event fired when the editor content (its DOM structure) is ready.
2020 * It is similar to the native `DOMContentLoaded` event, but it applies to
2021 * the editor content. It is also the first event fired after
2022 * the {@link CKEDITOR.editable} is initialized.
2023 *
2024 * This event is particularly important for classic (`iframe`-based)
2025 * editor, because on editor initialization and every time the data are set
2026 * (by {@link CKEDITOR.editor#method-setData}) content DOM structure
2027 * is rebuilt. Thus, e.g. you need to attach DOM event listeners
2028 * on editable one more time.
2029 *
2030 * For inline editor this event is fired only once &mdash; when the
2031 * editor is initialized for the first time. This is because setting
2032 * editor content does not cause editable destruction and creation.
2033 *
2034 * The {@link #contentDom} event goes along with {@link #contentDomUnload}
2035 * which is fired before the content DOM structure is destroyed. This is the
2036 * right moment to detach content DOM event listener. Otherwise
2037 * browsers like IE or Opera may throw exceptions when accessing
2038 * elements from the detached document.
2039 *
2040 * **Note:** {@link CKEDITOR.editable#attachListener} is a convenient
2041 * way to attach listeners that will be detached on {@link #contentDomUnload}.
2042 *
2043 * editor.on( 'contentDom', function() {
2044 * var editable = editor.editable();
2045 *
2046 * editable.attachListener( editable, 'click', function() {
2047 * console.log( 'The editable was clicked.' );
2048 * });
2049 * });
2050 *
2051 * @event contentDom
2052 * @param {CKEDITOR.editor} editor This editor instance.
2053 */
2054
2055 /**
2056 * Event fired before the content DOM structure is destroyed.
2057 * See {@link #contentDom} documentation for more details.
2058 *
2059 * @event contentDomUnload
2060 * @param {CKEDITOR.editor} editor This editor instance.
2061 */
2062
2063 /**
2064 * Event fired when the content DOM changes and some of the references as well as
2065 * the native DOM event listeners could be lost.
2066 * This event is useful when it is important to keep track of references
2067 * to elements in the editable content from code.
2068 *
2069 * @since 4.3
2070 * @event contentDomInvalidated
2071 * @param {CKEDITOR.editor} editor This editor instance.
2072 */