diff options
Diffstat (limited to 'core/editor.js')
| -rw-r--r-- | core/editor.js | 1438 |
1 files changed, 1438 insertions, 0 deletions
diff --git a/core/editor.js b/core/editor.js new file mode 100644 index 0000000..d55ef87 --- /dev/null +++ b/core/editor.js @@ -0,0 +1,1438 @@ +/** + * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.html or http://ckeditor.com/license + */ + +/** + * @fileOverview Defines the {@link CKEDITOR.editor} class that represents an + * editor instance. + */ + +(function() { + // Override the basic constructor defined at editor_basic.js. + Editor.prototype = CKEDITOR.editor.prototype; + CKEDITOR.editor = Editor; + + /** + * Represents an editor instance. This constructor should be rarely + * used in favor of the {@link CKEDITOR} editor creation functions. + * + * @class CKEDITOR.editor + * @mixins CKEDITOR.event + * @constructor Creates an editor class instance. + * @param {Object} [instanceConfig] Configuration values for this specific instance. + * @param {CKEDITOR.dom.element} [element] The DOM element upon which this editor + * will be created. + * @param {Number} [mode] The element creation mode to be used by this editor. + */ + function Editor( instanceConfig, element, mode ) { + // Call the CKEDITOR.event constructor to initialize this instance. + CKEDITOR.event.call( this ); + + // Make a clone of the config object, to avoid having it touched by our code. (#9636) + instanceConfig = instanceConfig && CKEDITOR.tools.clone( instanceConfig ); + + // if editor is created off one page element. + if ( element !== undefined ) { + // Asserting element and mode not null. + if ( !( element instanceof CKEDITOR.dom.element ) ) + throw new Error( 'Expect element of type CKEDITOR.dom.element.' ); + else if ( !mode ) + throw new Error( 'One of the element modes must be specified.' ); + + if ( CKEDITOR.env.ie && CKEDITOR.env.quirks && mode == CKEDITOR.ELEMENT_MODE_INLINE ) { + throw new Error( 'Inline element mode is not supported on IE quirks.' ); + } + + // Asserting element DTD depending on mode. + if ( mode == CKEDITOR.ELEMENT_MODE_INLINE && !element.is( CKEDITOR.dtd.$editable ) || mode == CKEDITOR.ELEMENT_MODE_REPLACE && element.is( CKEDITOR.dtd.$nonBodyContent ) ) + throw new Error( 'The specified element mode is not supported on element: "' + element.getName() + '".' ); + + + /** + * The original host page element upon which the editor is created, it's only + * supposed to be provided by the concrete editor creator and is not subjected to + * be modified. + * + * @readonly + * @property {CKEDITOR.dom.element} + */ + this.element = element; + + /** + * This property indicate the way how this instance is associated with the {@link #element}. + * + * @readonly + * @property {Number} + * @see CKEDITOR#ELEMENT_MODE_INLINE + * @see CKEDITOR#ELEMENT_MODE_REPLACE + */ + this.elementMode = mode; + + this.name = ( this.elementMode != CKEDITOR.ELEMENT_MODE_APPENDTO ) && ( element.getId() || element.getNameAtt() ); + } + else + this.elementMode = CKEDITOR.ELEMENT_MODE_NONE; + + // Declare the private namespace. + this._ = {}; + + this.commands = {}; + + /** + * Contains all UI templates created for this editor instance. + * + * @readonly + * @property {Object} + */ + this.templates = {}; + + /** + * A unique identifier of this editor instance. + * + * **Note:** It will be originated from the ID or name + * attribute of the {@link #element}, otherwise a name pattern of + * `'editor{n}'` will be used. + * + * @readonly + * @property {String} + */ + this.name = this.name || genEditorName(); + + /** + * A unique random string assigned to each editor instance in the page. + * + * @readonly + * @property {String} + */ + this.id = CKEDITOR.tools.getNextId(); + + /** + * Indicates editor initialization status. The following statuses are available: + * + * * **unloaded**: the initial state - editor's instance has been initialized, + * but its components (config, plugins, language files) are not loaded yet. + * * **loaded**: editor's components have been loaded - see {@link CKEDITOR.editor#loaded} event. + * * **ready**: editor is fully initialized and ready - see {@link CKEDITOR.editor#instanceReady} event. + * * **destroyed**: the editor has been destroyed - see {@link CKEDITOR.editor#method-destroy} method. + * + * @since 4.1 + * @readonly + * @property {String} + */ + this.status = 'unloaded'; + + /** + * The configurations for this editor instance. It inherits all + * settings defined in {@link CKEDITOR.config}, combined with settings + * loaded from custom configuration files and those defined inline in + * the page when creating the editor. + * + * var editor = CKEDITOR.instances.editor1; + * alert( editor.config.skin ); // e.g. 'moono' + * + * @readonly + * @property {CKEDITOR.config} + */ + this.config = CKEDITOR.tools.prototypedCopy( CKEDITOR.config ); + + /** + * Namespace containing UI features related to this editor instance. + * + * @readonly + * @property {CKEDITOR.ui} + */ + this.ui = new CKEDITOR.ui( this ); + + /** + * Controls the focus state of this editor instance. This property + * is rarely used for normal API operations. It is mainly + * destinated to developer adding UI elements to the editor interface. + * + * @readonly + * @property {CKEDITOR.focusManager} + */ + this.focusManager = new CKEDITOR.focusManager( this ); + + /** + * Controls keystrokes typing in this editor instance. + * + * @readonly + * @property {CKEDITOR.keystrokeHandler} + */ + this.keystrokeHandler = new CKEDITOR.keystrokeHandler( this ); + + // Make the editor update its command states on mode change. + this.on( 'readOnly', updateCommands ); + this.on( 'selectionChange', updateCommandsContext ); + this.on( 'mode', updateCommands ); + + // Handle startup focus. + this.on( 'instanceReady', function( event ) { + this.config.startupFocus && this.focus(); + } ); + + CKEDITOR.fire( 'instanceCreated', null, this ); + + // Add this new editor to the CKEDITOR.instances collections. + CKEDITOR.add( this ); + + // Return the editor instance immediately to enable early stage event registrations. + CKEDITOR.tools.setTimeout( function() { + initConfig( this, instanceConfig ); + }, 0, this ); + } + + var nameCounter = 0; + + function genEditorName() { + do { + var name = 'editor' + ( ++nameCounter ); + } + while ( CKEDITOR.instances[ name ] ) + + return name; + } + + function updateCommands() { + var commands = this.commands, + name; + + for ( name in commands ) + updateCommand( this, commands[ name ] ); + } + + function updateCommand( editor, cmd ) { + cmd[ cmd.startDisabled ? 'disable' : editor.readOnly && !cmd.readOnly ? 'disable' : cmd.modes[ editor.mode ] ? 'enable' : 'disable' ](); + } + + function updateCommandsContext( ev ) { + var command, + commands = this.commands, + editor = ev.editor, + path = ev.data.path; + + for ( var name in commands ) { + command = commands[ name ]; + + if ( command.contextSensitive ) + command.refresh( editor, path ); + } + } + + // ##### START: Config Privates + + // These function loads custom configuration files and cache the + // CKEDITOR.editorConfig functions defined on them, so there is no need to + // download them more than once for several instances. + var loadConfigLoaded = {}; + + function loadConfig( editor ) { + var customConfig = editor.config.customConfig; + + // Check if there is a custom config to load. + if ( !customConfig ) + return false; + + customConfig = CKEDITOR.getUrl( customConfig ); + + var loadedConfig = loadConfigLoaded[ customConfig ] || ( loadConfigLoaded[ customConfig ] = {} ); + + // If the custom config has already been downloaded, reuse it. + if ( loadedConfig.fn ) { + // Call the cached CKEDITOR.editorConfig defined in the custom + // config file for the editor instance depending on it. + loadedConfig.fn.call( editor, editor.config ); + + // If there is no other customConfig in the chain, fire the + // "configLoaded" event. + if ( CKEDITOR.getUrl( editor.config.customConfig ) == customConfig || !loadConfig( editor ) ) + editor.fireOnce( 'customConfigLoaded' ); + } else { + // Load the custom configuration file. + // To resolve customConfig race conflicts, use scriptLoader#queue + // instead of scriptLoader#load (#6504). + CKEDITOR.scriptLoader.queue( customConfig, function() { + // If the CKEDITOR.editorConfig function has been properly + // defined in the custom configuration file, cache it. + if ( CKEDITOR.editorConfig ) + loadedConfig.fn = CKEDITOR.editorConfig; + else + loadedConfig.fn = function() {}; + + // Call the load config again. This time the custom + // config is already cached and so it will get loaded. + loadConfig( editor ); + }); + } + + return true; + } + + function initConfig( editor, instanceConfig ) { + // Setup the lister for the "customConfigLoaded" event. + editor.on( 'customConfigLoaded', function() { + if ( instanceConfig ) { + // Register the events that may have been set at the instance + // configuration object. + if ( instanceConfig.on ) { + for ( var eventName in instanceConfig.on ) { + editor.on( eventName, instanceConfig.on[ eventName ] ); + } + } + + // Overwrite the settings from the in-page config. + CKEDITOR.tools.extend( editor.config, instanceConfig, true ); + + delete editor.config.on; + } + + onConfigLoaded( editor ); + }); + + // The instance config may override the customConfig setting to avoid + // loading the default ~/config.js file. + if ( instanceConfig && instanceConfig.customConfig != undefined ) + editor.config.customConfig = instanceConfig.customConfig; + + // Load configs from the custom configuration files. + if ( !loadConfig( editor ) ) + editor.fireOnce( 'customConfigLoaded' ); + } + + // ##### END: Config Privates + + function onConfigLoaded( editor ) { + // Set config related properties. + /** + * Indicates the read-only state of this editor. This is a read-only property. + * + * @since 3.6 + * @readonly + * @property {Boolean} + * @see CKEDITOR.editor#setReadOnly + */ + editor.readOnly = !!( editor.config.readOnly || ( editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE ? editor.element.isReadOnly() : editor.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE ? editor.element.getAttribute( 'disabled' ) : false ) ); + + /** + * Indicates that the editor is running into an environment where + * no block elements are accepted inside the content. + * + * @readonly + * @property {Boolean} + */ + editor.blockless = editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE && !CKEDITOR.dtd[ editor.element.getName() ][ 'p' ]; + + /** + * The [tabbing navigation](http://en.wikipedia.org/wiki/Tabbing_navigation) order determined for this editor instance. + * This can be set by the <code>{@link CKEDITOR.config#tabIndex}</code> + * setting or taken from the `tabindex` attribute of the + * {@link #element} associated with the editor. + * + * alert( editor.tabIndex ); // e.g. 0 + * + * @readonly + * @property {Number} [=0] + */ + editor.tabIndex = editor.config.tabIndex || editor.element && editor.element.getAttribute( 'tabindex' ) || 0; + + // Set CKEDITOR.skinName. Note that it is not possible to have + // different skins on the same page, so the last one to set it "wins". + if ( editor.config.skin ) + CKEDITOR.skinName = editor.config.skin; + + // Fire the "configLoaded" event. + editor.fireOnce( 'configLoaded' ); + + initComponents( editor ); + } + + // Various other core components that read editor configuration. + function initComponents( editor ) { + // Documented in dataprocessor.js. + editor.dataProcessor = new CKEDITOR.htmlDataProcessor( editor ); + // Documented in filter.js + editor.filter = new CKEDITOR.filter( editor ); + loadSkin( editor ); + } + + function loadSkin( editor ) { + CKEDITOR.skin.loadPart( 'editor', function() { + loadLang( editor ); + }); + } + + function loadLang( editor ) { + CKEDITOR.lang.load( editor.config.language, editor.config.defaultLanguage, function( languageCode, lang ) { + /** + * The code for the language resources that have been loaded + * for the user interface elements of this editor instance. + * + * alert( editor.langCode ); // e.g. 'en' + * + * @readonly + * @property {String} + */ + editor.langCode = languageCode; + + /** + * An object that contains all language strings used by the editor interface. + * + * alert( editor.lang.basicstyles.bold ); // e.g. 'Negrito' (if the language is set to Portuguese) + * + * @readonly + * @property {Object} lang + */ + // As we'll be adding plugin specific entries that could come + // from different language code files, we need a copy of lang, + // not a direct reference to it. + editor.lang = CKEDITOR.tools.prototypedCopy( lang ); + + // We're not able to support RTL in Firefox 2 at this time. + if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 && editor.lang.dir == 'rtl' ) + editor.lang.dir = 'ltr'; + + if ( !editor.config.contentsLangDirection ) { + // Fallback to either the editable element direction or editor UI direction depending on creators. + editor.config.contentsLangDirection = editor.elementMode == CKEDITOR.ELEMENT_MODE_INLINE ? editor.element.getDirection( 1 ) : editor.lang.dir; + } + + editor.fire( 'langLoaded' ); + + preloadStylesSet( editor ); + }); + } + + // Preloads styles set file (config.stylesSet). + // If stylesSet was defined directly (by an array) + // this function will call loadPlugins fully synchronously. + // If stylesSet is a string (path) loadPlugins will + // be called asynchronously. + // In both cases - styles will be preload before plugins initialization. + function preloadStylesSet( editor ) { + editor.getStylesSet( function( styles ) { + // Wait for editor#loaded, so plugins could add their listeners. + // But listen with high priority to fire editor#stylesSet before editor#uiReady and editor#setData. + editor.once( 'loaded', function() { + // Note: we can't use fireOnce because this event may canceled and fired again. + editor.fire( 'stylesSet', { styles: styles } ); + }, null, null, 1 ); + + loadPlugins( editor ); + } ); + } + + function loadPlugins( editor ) { + var config = editor.config, + plugins = config.plugins, + extraPlugins = config.extraPlugins, + removePlugins = config.removePlugins; + + if ( extraPlugins ) { + // Remove them first to avoid duplications. + var extraRegex = new RegExp( '(?:^|,)(?:' + extraPlugins.replace( /\s*,\s*/g, '|' ) + ')(?=,|$)', 'g' ); + plugins = plugins.replace( extraRegex, '' ); + + plugins += ',' + extraPlugins; + } + + if ( removePlugins ) { + var removeRegex = new RegExp( '(?:^|,)(?:' + removePlugins.replace( /\s*,\s*/g, '|' ) + ')(?=,|$)', 'g' ); + plugins = plugins.replace( removeRegex, '' ); + } + + // Load the Adobe AIR plugin conditionally. + CKEDITOR.env.air && ( plugins += ',adobeair' ); + + // Load all plugins defined in the "plugins" setting. + CKEDITOR.plugins.load( plugins.split( ',' ), function( plugins ) { + // The list of plugins. + var pluginsArray = []; + + // The language code to get loaded for each plugin. Null + // entries will be appended for plugins with no language files. + var languageCodes = []; + + // The list of URLs to language files. + var languageFiles = []; + + /** + * An object that contains references to all plugins used by this + * editor instance. + * + * alert( editor.plugins.dialog.path ); // e.g. 'http://example.com/ckeditor/plugins/dialog/' + * + * // Check if a plugin is available. + * if ( editor.plugins.image ) { + * ... + * } + * + * @readonly + * @property {Object} + */ + editor.plugins = plugins; + + // Loop through all plugins, to build the list of language + // files to get loaded. + // + // Check also whether any of loaded plugins doesn't require plugins + // defined in config.removePlugins. Throw non-blocking error if this happens. + for ( var pluginName in plugins ) { + var plugin = plugins[ pluginName ], + pluginLangs = plugin.lang, + lang = null, + requires = plugin.requires, + match, name; + + // Transform it into a string, if it's not one. + if ( CKEDITOR.tools.isArray( requires ) ) + requires = requires.join( ',' ); + + if ( requires && ( match = requires.match( removeRegex ) ) ) { + while ( ( name = match.pop() ) ) { + CKEDITOR.tools.setTimeout( function( name, pluginName ) { + throw new Error( 'Plugin "' + name.replace( ',', '' ) + '" cannot be removed from the plugins list, because it\'s required by "' + pluginName + '" plugin.'); + }, 0, null, [ name, pluginName ] ); + } + } + + // If the plugin has "lang". + if ( pluginLangs && !editor.lang[ pluginName ] ) { + // Trasnform the plugin langs into an array, if it's not one. + if ( pluginLangs.split ) + pluginLangs = pluginLangs.split( ',' ); + + // Resolve the plugin language. If the current language + // is not available, get English or the first one. + if ( CKEDITOR.tools.indexOf( pluginLangs, editor.langCode ) >= 0 ) + lang = editor.langCode; + else { + // The language code may have the locale information (zh-cn). + // Fall back to locale-less in that case (zh). + var langPart = editor.langCode.replace( /-.*/, '' ); + if ( langPart != editor.langCode && CKEDITOR.tools.indexOf( pluginLangs, langPart ) >= 0 ) + lang = langPart; + // Try the only "generic" option we have: English. + else if ( CKEDITOR.tools.indexOf( pluginLangs, 'en' ) >= 0 ) + lang = 'en'; + else + lang = pluginLangs[ 0 ]; + } + + if ( !plugin.langEntries || !plugin.langEntries[ lang ] ) { + // Put the language file URL into the list of files to + // get downloaded. + languageFiles.push( CKEDITOR.getUrl( plugin.path + 'lang/' + lang + '.js' ) ); + } else { + editor.lang[ pluginName ] = plugin.langEntries[ lang ]; + lang = null; + } + } + + // Save the language code, so we know later which + // language has been resolved to this plugin. + languageCodes.push( lang ); + + pluginsArray.push( plugin ); + } + + // Load all plugin specific language files in a row. + CKEDITOR.scriptLoader.load( languageFiles, function() { + // Initialize all plugins that have the "beforeInit" and "init" methods defined. + var methods = [ 'beforeInit', 'init', 'afterInit' ]; + for ( var m = 0; m < methods.length; m++ ) { + for ( var i = 0; i < pluginsArray.length; i++ ) { + var plugin = pluginsArray[ i ]; + + // Uses the first loop to update the language entries also. + if ( m === 0 && languageCodes[ i ] && plugin.lang && plugin.langEntries ) + editor.lang[ plugin.name ] = plugin.langEntries[ languageCodes[ i ] ]; + + // Call the plugin method (beforeInit and init). + if ( plugin[ methods[ m ] ] ) + plugin[ methods[ m ] ]( editor ); + } + } + + editor.fireOnce( 'pluginsLoaded' ); + + // Setup the configured keystrokes. + config.keystrokes && editor.setKeystroke( editor.config.keystrokes ); + + // Setup the configured blocked keystrokes. + for ( i = 0; i < editor.config.blockedKeystrokes.length; i++ ) + editor.keystrokeHandler.blockedKeystrokes[ editor.config.blockedKeystrokes[ i ] ] = 1; + + editor.status = 'loaded'; + editor.fireOnce( 'loaded' ); + CKEDITOR.fire( 'instanceLoaded', null, editor ); + }); + }); + } + + // Send to data output back to editor's associated element. + function updateEditorElement() { + var element = this.element; + + // Some editor creation mode will not have the + // associated element. + if ( element && this.elementMode != CKEDITOR.ELEMENT_MODE_APPENDTO ) { + var data = this.getData(); + + if ( this.config.htmlEncodeOutput ) + data = CKEDITOR.tools.htmlEncode( data ); + + if ( element.is( 'textarea' ) ) + element.setValue( data ); + else + element.setHtml( data ); + + return true; + } + return false; + } + + CKEDITOR.tools.extend( CKEDITOR.editor.prototype, { + /** + * Adds a command definition to the editor instance. Commands added with + * this function can be executed later with the <code>{@link #execCommand}</code> method. + * + * editorInstance.addCommand( 'sample', { + * exec: function( editor ) { + * alert( 'Executing a command for the editor name "' + editor.name + '"!' ); + * } + * } ); + * + * @param {String} commandName The indentifier name of the command. + * @param {CKEDITOR.commandDefinition} commandDefinition The command definition. + */ + addCommand: function( commandName, commandDefinition ) { + commandDefinition.name = commandName.toLowerCase(); + var cmd = new CKEDITOR.command( this, commandDefinition ); + + // Update command when mode is set. + // This guarantees that commands added before first editor#mode + // aren't immediately updated, but waits for editor#mode and that + // commands added later are immediately refreshed, even when added + // before instanceReady. #10103, #10249 + if ( this.mode ) + updateCommand( this, cmd ); + + return this.commands[ commandName ] = cmd; + }, + + /** + * Destroys the editor instance, releasing all resources used by it. + * If the editor replaced an element, the element will be recovered. + * + * alert( CKEDITOR.instances.editor1 ); // e.g. object + * CKEDITOR.instances.editor1.destroy(); + * alert( CKEDITOR.instances.editor1 ); // undefined + * + * @param {Boolean} [noUpdate] If the instance is replacing a DOM + * element, this parameter indicates whether or not to update the + * element with the instance contents. + */ + destroy: function( noUpdate ) { + this.fire( 'beforeDestroy' ); + + !noUpdate && updateEditorElement.call( this ); + + this.editable( null ); + + this.status = 'destroyed'; + + this.fire( 'destroy' ); + + // Plug off all listeners to prevent any further events firing. + this.removeAllListeners(); + + CKEDITOR.remove( this ); + CKEDITOR.fire( 'instanceDestroyed', null, this ); + }, + + /** + * @param {CKEDITOR.dom.node} [startNode] From which the path should start, if not specified default to editor selection's + * start element yield by {@link CKEDITOR.dom.selection#getStartElement}. + * @returns {CKEDITOR.dom.elementPath} + * @see CKEDITOR.dom.elementPath + */ + elementPath: function( startNode ) { + startNode = startNode || this.getSelection().getStartElement(); + return startNode ? new CKEDITOR.dom.elementPath( startNode, this.editable() ) : null; + }, + + /** + * Shortcut to create a {@link CKEDITOR.dom.range} instance from the editable element. + * + * @returns {CKEDITOR.dom.range} The dom range created if the editable has presented. + * @see CKEDITOR.dom.range + */ + createRange: function() { + var editable = this.editable(); + return editable ? new CKEDITOR.dom.range( editable ) : null; + }, + + /** + * Executes a command associated with the editor. + * + * editorInstance.execCommand( 'bold' ); + * + * @param {String} commandName The indentifier name of the command. + * @param {Object} [data] Data to be passed to the command. + * @returns {Boolean} `true` if the command was executed + * successfully, otherwise `false`. + * @see CKEDITOR.editor#addCommand + */ + execCommand: function( commandName, data ) { + var command = this.getCommand( commandName ); + + var eventData = { + name: commandName, + commandData: data, + command: command + }; + + if ( command && command.state != CKEDITOR.TRISTATE_DISABLED ) { + if ( this.fire( 'beforeCommandExec', eventData ) !== true ) { + eventData.returnValue = command.exec( eventData.commandData ); + + // Fire the 'afterCommandExec' immediately if command is synchronous. + if ( !command.async && this.fire( 'afterCommandExec', eventData ) !== true ) + return eventData.returnValue; + } + } + + // throw 'Unknown command name "' + commandName + '"'; + return false; + }, + + /** + * Gets one of the registered commands. Note that after registering a + * command definition with {@link #addCommand}, it is + * transformed internally into an instance of + * {@link CKEDITOR.command}, which will then be returned by this function. + * + * @param {String} commandName The name of the command to be returned. + * This is the same name that is used to register the command with `addCommand`. + * @returns {CKEDITOR.command} The command object identified by the provided name. + */ + getCommand: function( commandName ) { + return this.commands[ commandName ]; + }, + + /** + * Gets the editor data. The data will be in raw format. It is the same + * data that is posted by the editor. + * + * if ( CKEDITOR.instances.editor1.getData() == '' ) + * alert( 'There is no data available' ); + * + * @returns {String} The editor data. + */ + getData: function( noEvents ) { + !noEvents && this.fire( 'beforeGetData' ); + + var eventData = this._.data; + + if ( typeof eventData != 'string' ) { + var element = this.element; + if ( element && this.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE ) + eventData = element.is( 'textarea' ) ? element.getValue() : element.getHtml(); + else + eventData = ''; + } + + eventData = { dataValue: eventData }; + + // Fire "getData" so data manipulation may happen. + !noEvents && this.fire( 'getData', eventData ); + + return eventData.dataValue; + }, + + /** + * Gets the "raw data" currently available in the editor. This is a + * fast method which returns the data as is, without processing, so it is + * not recommended to use it on resulting pages. Instead it can be used + * combined with the {@link #method-loadSnapshot} method in order + * to be able to automatically save the editor data from time to time + * while the user is using the editor, to avoid data loss, without risking + * performance issues. + * + * alert( editor.getSnapshot() ); + * + * @see CKEDITOR.editor#getData + */ + getSnapshot: function() { + var data = this.fire( 'getSnapshot' ); + + if ( typeof data != 'string' ) { + var element = this.element; + if ( element && this.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE ) + data = element.is( 'textarea' ) ? element.getValue() : element.getHtml(); + } + + return data; + }, + + /** + * Loads "raw data" into the editor. The data is loaded with processing + * straight to the editing area. It should not be used as a way to load + * any kind of data, but instead in combination with + * {@link #method-getSnapshot} produced data. + * + * var data = editor.getSnapshot(); + * editor.loadSnapshot( data ); + * + * @see CKEDITOR.editor#setData + */ + loadSnapshot: function( snapshot ) { + this.fire( 'loadSnapshot', snapshot ); + }, + + /** + * Sets the editor data. The data must be provided in the raw format (HTML). + * + * Note that this method is asynchronous. The `callback` parameter must + * be used if interaction with the editor is needed after setting the data. + * + * CKEDITOR.instances.editor1.setData( '<p>This is the editor data.</p>' ); + * + * CKEDITOR.instances.editor1.setData( '<p>Some other editor data.</p>', function() { + * this.checkDirty(); // true + * }); + * + * @param {String} data HTML code to replace the curent content in the editor. + * @param {Function} callback Function to be called after the `setData` is completed. + * @param {Boolean} internal Whether to suppress any event firing when copying data internally inside the editor. + */ + setData: function( data, callback, internal ) { + if ( callback ) { + this.on( 'dataReady', function( evt ) { + evt.removeListener(); + callback.call( evt.editor ); + }); + } + + // Fire "setData" so data manipulation may happen. + var eventData = { dataValue: data }; + !internal && this.fire( 'setData', eventData ); + + this._.data = eventData.dataValue; + + !internal && this.fire( 'afterSetData', eventData ); + }, + + /** + * Puts or restores the editor into read-only state. When in read-only, + * the user is not able to change the editor contents, but can still use + * some editor features. This function sets the {@link #property-readOnly} + * property of the editor, firing the {@link #event-readOnly} event. + * + * **Note:** the current editing area will be reloaded. + * + * @since 3.6 + * @param {Boolean} [isReadOnly] Indicates that the editor must go + * read-only (`true`, default) or be restored and made editable (`false`). + */ + setReadOnly: function( isReadOnly ) { + isReadOnly = ( isReadOnly == undefined ) || isReadOnly; + + if ( this.readOnly != isReadOnly ) { + this.readOnly = isReadOnly; + + // Block or release BACKSPACE key according to current read-only + // state to prevent browser's history navigation (#9761). + this.keystrokeHandler.blockedKeystrokes[ 8 ] = +isReadOnly; + + this.editable().setReadOnly( isReadOnly ); + + // Fire the readOnly event so the editor features can update + // their state accordingly. + this.fire( 'readOnly' ); + } + }, + + /** + * Inserts HTML code into the currently selected position in the editor in WYSIWYG mode. + * + * * `"html"` - content being inserted will completely override styles + * of selected position. + * * `"unfiltered_html"` - like `"html"` but content isn't filtered with {@link CKEDITOR.filter}. + * * `"text"` - content being inserted will inherit styles applied in + * selected position. This mode should be used when inserting "htmlified" plain text + * (HTML without inline styles and styling elements like + * `<b/>, <strong/>, <span style="..."/>`). + * + * Example: + * + * CKEDITOR.instances.editor1.insertHtml( '<p>This is a new paragraph.</p>' ); + * + * @param {String} html HTML code to be inserted into the editor. + * @param {String} [mode='html'] Mode in which HTML will be inserted. + */ + insertHtml: function( html, mode ) { + this.fire( 'insertHtml', { dataValue: html, mode: mode } ); + }, + + /** + * Insert text content into the currently selected position in the + * editor in WYSIWYG mode. The styles of the selected element will be applied to the inserted text. + * Spaces around the text will be leaving untouched. + * + * CKEDITOR.instances.editor1.insertText( ' line1 \n\n line2' ); + * + * @since 3.5 + * @param {String} text Text to be inserted into the editor. + */ + insertText: function( text ) { + this.fire( 'insertText', text ); + }, + + /** + * Inserts an element into the currently selected position in the + * editor in WYSIWYG mode. + * + * var element = CKEDITOR.dom.element.createFromHtml( '<img src="hello.png" border="0" title="Hello" />' ); + * CKEDITOR.instances.editor1.insertElement( element ); + * + * @param {CKEDITOR.dom.element} element The element to be inserted + * into the editor. + */ + insertElement: function( element ) { + this.fire( 'insertElement', element ); + }, + + /** + * Moves the selection focus to the editing area space in the editor. + */ + focus: function() { + this.fire( 'beforeFocus' ); + }, + + /** + * Checks whether the current editor contents present changes when + * compared to the contents loaded into the editor at startup, or to + * the contents available in the editor when {@link #resetDirty} + * was called. + * + * function beforeUnload( evt ) { + * if ( CKEDITOR.instances.editor1.checkDirty() ) + * return evt.returnValue = "You will lose the changes made in the editor."; + * } + * + * if ( window.addEventListener ) + * window.addEventListener( 'beforeunload', beforeUnload, false ); + * else + * window.attachEvent( 'onbeforeunload', beforeUnload ); + * + * @returns {Boolean} `true` if the contents contain changes. + */ + checkDirty: function() { + return this.status == 'ready' && this._.previousValue !== this.getSnapshot(); + }, + + /** + * Resets the "dirty state" of the editor so subsequent calls to + * {@link #checkDirty} will return `false` if the user will not + * have made further changes to the contents. + * + * alert( editor.checkDirty() ); // e.g. true + * editor.resetDirty(); + * alert( editor.checkDirty() ); // false + */ + resetDirty: function() { + this._.previousValue = this.getSnapshot(); + }, + + /** + * Updates the <code><textarea></code> element that was replaced by the editor with + * the current data available in the editor. + * + * **Note:** This method will only affect those editor instances created + * with {@link CKEDITOR#ELEMENT_MODE_REPLACE} element mode. + * + * CKEDITOR.instances.editor1.updateElement(); + * alert( document.getElementById( 'editor1' ).value ); // The current editor data. + * + * @see CKEDITOR.editor#element + */ + updateElement: function() { + return updateEditorElement.call( this ); + }, + + /** + * Assigns keystrokes associated to editor commands. + * + * editor.setKeystroke( CKEDITOR.CTRL + 115, 'save' ); // Assigned CTRL+S to "save" command. + * editor.setKeystroke( CKEDITOR.CTRL + 115, false ); // Disabled CTRL+S keystroke assignment. + * editor.setKeystroke( [ + * [ CKEDITOR.ALT + 122, false ], + * [ CKEDITOR.CTRL + 121, 'link' ], + * [ CKEDITOR.SHIFT + 120, 'bold' ] + * ] ); + * + * This method may be used in the following cases: + * + * * By plugins (like `link` or `basicstyles`) to set their keystrokes when plugins are being loaded. + * * During the runtime to modify existing keystrokes. + * + * The editor handles keystroke configuration in the following order: + * + * 1. Plugins use this method to define default keystrokes. + * 2. Editor extends default keystrokes with {@link CKEDITOR.config#keystrokes}. + * 3. Editor blocks keystrokes defined in {@link CKEDITOR.config#blockedKeystrokes}. + * + * After all, you can still set new keystrokes using this method during the runtime. + * + * @since 4.0 + * @param {Number/Array} keystroke Keystroke or an array of keystroke definitions. + * @param {String/Boolean} [behavior] A command to be executed on the keystroke. + */ + setKeystroke: function() { + var keystrokes = this.keystrokeHandler.keystrokes, + newKeystrokes = CKEDITOR.tools.isArray( arguments[ 0 ] ) ? arguments[ 0 ] : [ [].slice.call( arguments, 0 ) ], + keystroke, behavior; + + for ( var i = newKeystrokes.length; i--; ) { + keystroke = newKeystrokes[ i ]; + behavior = 0; + + // It may be a pair of: [ key, command ] + if ( CKEDITOR.tools.isArray( keystroke ) ) { + behavior = keystroke[ 1 ]; + keystroke = keystroke[ 0 ]; + } + + if ( behavior ) + keystrokes[ keystroke ] = behavior; + else + delete keystrokes[ keystroke ]; + } + }, + + /** + * Shorthand for {@link CKEDITOR.filter#addFeature}. + * + * @param {CKEDITOR.feature} feature See {@link CKEDITOR.filter#addFeature}. + * @returns {Boolean} See {@link CKEDITOR.filter#addFeature}. + */ + addFeature: function( feature ) { + return this.filter.addFeature( feature ); + } + }); +})(); + +/** + * The editor has no associated element. + * + * @readonly + * @property {Number} [=0] + * @member CKEDITOR + */ +CKEDITOR.ELEMENT_MODE_NONE = 0; + +/** + * The element is to be replaced by the editor instance. + * + * @readonly + * @property {Number} [=1] + * @member CKEDITOR + */ +CKEDITOR.ELEMENT_MODE_REPLACE = 1; + +/** + * The editor is to be created inside the element. + * + * @readonly + * @property {Number} [=2] + * @member CKEDITOR + */ +CKEDITOR.ELEMENT_MODE_APPENDTO = 2; + +/** + * The editor is to be attached to the element, using it as the editing block. + * + * @readonly + * @property {Number} [=3] + * @member CKEDITOR + */ +CKEDITOR.ELEMENT_MODE_INLINE = 3; + +/** + * Whether to escape HTML when the editor updates the original input element. + * + * config.htmlEncodeOutput = true; + * + * @since 3.1 + * @cfg {Boolean} [htmlEncodeOutput=false] + * @member CKEDITOR.config + */ + +/** + * If `true`, makes the editor start in read-only state. Otherwise, it will check + * if the linked `<textarea>` element has the `disabled` attribute. + * + * config.readOnly = true; + * + * @since 3.6 + * @cfg {Boolean} [readOnly=false] + * @member CKEDITOR.config + * @see CKEDITOR.editor#setReadOnly + */ + +/** + * Sets whether the editable should have the focus when editor is loading for the first time. + * + * config.startupFocus = true; + * + * @cfg {Boolean} [startupFocus=false] + * @member CKEDITOR.config + */ + +/** + * Sets listeners on editor's events. + * + * **Note:** This property can be set only in `config` object passed directly + * to the {@link CKEDITOR#replace}, {@link CKEDITOR#inline} and other creators. + * + * CKEDITOR.replace( 'editor1', { + * on: { + * instanceReady: function() { + * alert( this.name ); // 'editor1' + * }, + * + * key: function() { + * // ... + * } + * } + * } ); + * + * @cfg {Object} on + * @member CKEDITOR.config + */ + +/** + * The outer most element in the DOM tree in which the editable element resides, it's provided + * by the concrete editor creator after editor UI is created and is not subjected to + * be modified. + * + * var editor = CKEDITOR.instances.editor1; + * alert( editor.container.getName() ); // 'span' + * + * @readonly + * @property {CKEDITOR.dom.element} container + */ + +/** + * The document that stores the editor contents. + * + * * For the framed editor it is equal to the document inside the + * iframe containing the editable element. + * * For the inline editor it is equal to {@link CKEDITOR#document}. + * + * The document object is available after the {@link #contentDom} event is fired + * and may be invalidated when the {@link #contentDomUnload} event is fired + * (framed editor only). + * + * editor.on( 'contentDom', function() { + * console.log( editor.document ); + * } ); + * + * @readonly + * @property {CKEDITOR.dom.document} document + */ + +/** + * The window instance related to the {@link #document} property. + * + * It is always equal to the `editor.document.getWindow()`. + * + * See {@link #document} property documentation. + * + * @readonly + * @property {CKEDITOR.dom.window} window + */ + +/** + * Fired when a CKEDITOR instance is created, but still before initializing it. + * To interact with a fully initialized instance, use the + * {@link CKEDITOR#instanceReady} event instead. + * + * @event instanceCreated + * @member CKEDITOR + * @param {CKEDITOR.editor} editor The editor instance that has been created. + */ + +/** + * Fired when CKEDITOR instance's components (config, languages and plugins) are fully + * loaded and initialized. However, the editor will be fully ready to for interaction + * on {@link CKEDITOR#instanceReady}. + * + * @event instanceLoaded + * @member CKEDITOR + * @param {CKEDITOR.editor} editor This editor instance that has been loaded. + */ + +/** + * Fired when a CKEDITOR instance is destroyed. + * + * @event instanceDestroyed + * @member CKEDITOR + * @param {CKEDITOR.editor} editor The editor instance that has been destroyed. + */ + +/** + * Fired when a CKEDITOR instance is created, fully initialized and ready for interaction. + * + * @event instanceReady + * @member CKEDITOR + * @param {CKEDITOR.editor} editor The editor instance that has been created. + */ + +/** + * Fired when the language is loaded into the editor instance. + * + * @since 3.6.1 + * @event langLoaded + * @param {CKEDITOR.editor} editor This editor instance. + */ + +/** + * Fired when all plugins are loaded and initialized into the editor instance. + * + * @event pluginsLoaded + * @param {CKEDITOR.editor} editor This editor instance. + */ + +/** + * Fired when styles set is loaded. During editor initialization + * phase the {@link #getStylesSet} method returns only styles that + * are already loaded, which may not include e.g. styles parsed + * by `stylesheetparser` plugin. Thus, to be notified when all + * styles are ready you can listen on this event. + * + * @since 4.1 + * @event stylesSet + * @param {CKEDITOR.editor} editor This editor instance. + * @param {Array} styles Array of styles definitions. + */ + +/** + * Fired before the command execution when {@link #execCommand} is called. + * + * @event beforeCommandExec + * @param {CKEDITOR.editor} editor This editor instance. + * @param data + * @param {String} data.name The command name. + * @param {Object} data.commandData The data to be sent to the command. This + * can be manipulated by the event listener. + * @param {CKEDITOR.command} data.command The command itself. + */ + +/** + * Fired after the command execution when {@link #execCommand} is called. + * + * @event afterCommandExec + * @param {CKEDITOR.editor} editor This editor instance. + * @param data + * @param {String} data.name The command name. + * @param {Object} data.commandData The data sent to the command. + * @param {CKEDITOR.command} data.command The command itself. + * @param {Object} data.returnValue The value returned by the command execution. + */ + +/** + * Fired when the custom configuration file is loaded, before the final + * configurations initialization. + * + * Custom configuration files can be loaded thorugh the + * {@link CKEDITOR.config#customConfig} setting. Several files can be loaded + * by changing this setting. + * + * @event customConfigLoaded + * @param {CKEDITOR.editor} editor This editor instance. + */ + +/** + * Fired once the editor configuration is ready (loaded and processed). + * + * @event configLoaded + * @param {CKEDITOR.editor} editor This editor instance. + */ + +/** + * Fired when this editor instance is destroyed. The editor at this + * point is not usable and this event should be used to perform the clean-up + * in any plugin. + * + * @event destroy + * @param {CKEDITOR.editor} editor This editor instance. + */ + +/** + * Internal event to get the current data. + * + * @event beforeGetData + * @param {CKEDITOR.editor} editor This editor instance. + */ + +/** + * Internal event to perform the {@link #method-getSnapshot} call. + * + * @event getSnapshot + * @param {CKEDITOR.editor} editor This editor instance. + */ + +/** + * Internal event to perform the {@link #method-loadSnapshot} call. + * + * @event loadSnapshot + * @param {CKEDITOR.editor} editor This editor instance. + * @param {String} data The data that will be used. + */ + +/** + * Event fired before the {@link #method-getData} call returns allowing additional manipulation. + * + * @event getData + * @param {CKEDITOR.editor} editor This editor instance. + * @param data + * @param {String} data.dataValue The data that will be returned. + */ + +/** + * Event fired before the {@link #method-setData} call is executed allowing additional manipulation. + * + * @event setData + * @param {CKEDITOR.editor} editor This editor instance. + * @param data + * @param {String} data.dataValue The data that will be used. + */ + +/** + * Event fired at the end of the {@link #method-setData} call execution. Usually it is better to use the + * {@link #dataReady} event. + * + * @event afterSetData + * @param {CKEDITOR.editor} editor This editor instance. + * @param data + * @param {String} data.dataValue The data that has been set. + */ + +/** + * Fired as an indicator of the editor data loading. It may be the result of + * calling {@link #method-setData} explicitly or an internal + * editor function, like the editor editing mode switching (move to Source and back). + * + * @event dataReady + * @param {CKEDITOR.editor} editor This editor instance. + */ + +/** + * Fired when the CKEDITOR instance is completely created, fully initialized + * and ready for interaction. + * + * @event instanceReady + * @param {CKEDITOR.editor} editor This editor instance. + */ + +/** + * Fired when editor's components (config, languages and plugins) are fully + * loaded and initialized. However, the editor will be fully ready to for interaction + * on {@link #instanceReady}. + * + * @event loaded + * @param {CKEDITOR.editor} editor This editor instance. + */ + +/** + * Internal event to perform the {@link #method-insertHtml} call. + * + * @event insertHtml + * @param {CKEDITOR.editor} editor This editor instance. + * @param data + * @param {String} data.mode Mode in which data is inserted (see {@link #method-insertHtml}). + * @param {String} data.dataValue The HTML to insert. + */ + +/** + * Internal event to perform the {@link #method-insertText} call. + * + * @event insertText + * @param {CKEDITOR.editor} editor This editor instance. + * @param {String} data The text to insert. + */ + +/** + * Internal event to perform the {@link #method-insertElement} call. + * + * @event insertElement + * @param {CKEDITOR.editor} editor This editor instance. + * @param {CKEDITOR.dom.element} data The element to insert. + */ + +/** + * Event fired after the {@link #property-readOnly} property changes. + * + * @since 3.6 + * @event readOnly + * @param {CKEDITOR.editor} editor This editor instance. + */ + +/** + * Event fired when an UI template is added to the editor instance. It makes + * it possible to bring customizations to the template source. + * + * @event template + * @param {CKEDITOR.editor} editor This editor instance. + * @param data + * @param {String} data.name The template name. + * @param {String} data.source The source data for this template. + */ + +/** + * Fired when content of the editor (its DOM structure) is ready. + * It is similar to native DOMContentLoaded event, but it concerns + * editor's content. It is also a first event fired after + * {@link CKEDITOR.editable} is initialized. + * + * This event is particularly important for framed editor, because + * on editor initialization and every time data are set + * (by {@link CKEDITOR.editor#method-setData}) contents DOM structure + * is rebuilt. Thus, e.g. you need to attach DOM events listeners + * on editable one more time. + * + * On inline editor this event is fired only once - when editor + * is initialized for the first time. That's because setting + * editor's content doesn't cause editable destruction and creation. + * + * {@link #contentDom} goes along with {@link #contentDomUnload} which + * is fired before contents DOM structure is destroyed. This is the + * right moment to detach content DOM events listener. Otherwise + * browsers like IE or Opera may throw exceptions when accessing + * elements from detached document. + * + * **Note:** {@link CKEDITOR.editable#attachListener} is a convenient + * way to attach listeners that will be detached on {@link #contentDomUnload}. + * + * editor.on( 'contentDom', function() { + * var editable = editor.editable(); + * + * editable.attachListener( editable, 'click', function() { + * console.log( 'Editable has been clicked' ); + * }); + * }); + * + * @event contentDom + * @param {CKEDITOR.editor} editor This editor instance. + */ + +/** + * Fired before contents DOM structure is destroyed. + * See {@link #contentDom} documentation for more details. + * + * @event contentDomUnload + * @param {CKEDITOR.editor} editor This editor instance. + */
\ No newline at end of file |
