diff options
Diffstat (limited to 'core/creators/themedui.js')
| -rw-r--r-- | core/creators/themedui.js | 457 |
1 files changed, 457 insertions, 0 deletions
diff --git a/core/creators/themedui.js b/core/creators/themedui.js new file mode 100644 index 0000000..72f9e0e --- /dev/null +++ b/core/creators/themedui.js @@ -0,0 +1,457 @@ +/** + * @license Copyright (c) 2003-2013, CKSource - Frederico Knabben. All rights reserved. + * For licensing, see LICENSE.html or http://ckeditor.com/license + */ + + /** @class CKEDITOR */ + +/** + * The class name used to identify `<textarea>` elements to be replace + * by CKEditor instances. Set it to empty/null to disable this feature. + * + * CKEDITOR.replaceClass = 'rich_editor'; + * + * @cfg {String} [replaceClass='ckeditor'] + */ +CKEDITOR.replaceClass = 'ckeditor'; + +(function() { + /** + * Replaces a `<textarea>` or a DOM element (`<div>`) with a CKEditor + * instance. For textareas, the initial value in the editor will be the + * textarea value. For DOM elements, their innerHTML will be used + * instead. We recommend using `<textarea>` and `<div>` elements only. + * + * <textarea id="myfield" name="myfield"></textarea> + * ... + * CKEDITOR.replace( 'myfield' ); + * + * var textarea = document.body.appendChild( document.createElement( 'textarea' ) ); + * CKEDITOR.replace( textarea ); + * + * @param {Object/String} element The DOM element (textarea), its ID or name. + * @param {Object} [config] The specific configurations to apply to this + * editor instance. Configurations set here will override global CKEditor settings (see {@link CKEDITOR.config}). + * @returns {CKEDITOR.editor} The editor instance created. + */ + CKEDITOR.replace = function( element, config ) { + return createInstance( element, config, null, CKEDITOR.ELEMENT_MODE_REPLACE ); + }; + + /** + * Creates a new editor at the end of a specific DOM element. + * + * <div id="editorSpace"></div> + * ... + * CKEDITOR.appendTo( 'editorSpace' ); + * + * @param {Object/String} element The DOM element, its ID or name. + * @param {Object} [config] The specific configurations to apply to this + * editor instance. Configurations set here will override global CKEditor settings. + * @param {String} [data] Since 3.3. Initial value for the instance. + * @returns {CKEDITOR.editor} The editor instance created. + */ + CKEDITOR.appendTo = function( element, config, data ) + { + return createInstance( element, config, data, CKEDITOR.ELEMENT_MODE_APPENDTO ); + }; + + /** + * Replace all `<textarea>` elements available in the document with + * editor instances. + * + * // Replace all <textarea> elements in the page. + * CKEDITOR.replaceAll(); + * + * // Replace all <textarea class="myClassName"> elements in the page. + * CKEDITOR.replaceAll( 'myClassName' ); + * + * // Selectively replace <textarea> elements, based on custom assertions. + * CKEDITOR.replaceAll( function( textarea, config ) { + * // Custom code to evaluate the replace, returning false + * // if it must not be done. + * // It also passes the "config" parameter, so the + * // developer can customize the instance. + * } ); + */ + CKEDITOR.replaceAll = function() { + var textareas = document.getElementsByTagName( 'textarea' ); + + for ( var i = 0; i < textareas.length; i++ ) { + var config = null, + textarea = textareas[ i ]; + + // The "name" and/or "id" attribute must exist. + if ( !textarea.name && !textarea.id ) + continue; + + if ( typeof arguments[ 0 ] == 'string' ) { + // The textarea class name could be passed as the function + // parameter. + + var classRegex = new RegExp( '(?:^|\\s)' + arguments[ 0 ] + '(?:$|\\s)' ); + + if ( !classRegex.test( textarea.className ) ) + continue; + } else if ( typeof arguments[ 0 ] == 'function' ) { + // An assertion function could be passed as the function parameter. + // It must explicitly return "false" to ignore a specific <textarea>. + config = {}; + if ( arguments[ 0 ]( textarea, config ) === false ) + continue; + } + + this.replace( textarea, config ); + } + }; + + /** @class CKEDITOR.editor */ + + /** + * Registers an editing mode. This function is to be used mainly by plugins. + * + * @param {String} mode The mode name. + * @param {Function} exec Function that perform the actual mode change. + */ + CKEDITOR.editor.prototype.addMode = function( mode, exec ) { + ( this._.modes || ( this._.modes = {} ) )[ mode ] = exec; + }; + + /** + * Change the editing mode of this editor instance. + * + * **Note:** The mode switch could be asynchronous depending on the mode provider, + * use the `callback` to hook subsequent code. + * + * // Switch to "source" view. + * CKEDITOR.instances.editor1.setMode( 'source' ); + * // Switch to "wysiwyg" and be noticed on completed. + * CKEDITOR.instances.editor1.setMode( 'wysiwyg', function() { alert( 'wysiwyg mode loaded!' ); } ); + * + * @param {String} [newMode] If not specified the {@link CKEDITOR.config#startupMode} will be used. + * @param {Function} [callback] Optional callback function which invoked once the mode switch has succeeded. + */ + CKEDITOR.editor.prototype.setMode = function( newMode, callback ) { + var editor = this; + + var modes = this._.modes; + + // Mode loading quickly fails. + if ( newMode == editor.mode || !modes || !modes[ newMode ] ) + return; + + editor.fire( 'beforeSetMode', newMode ); + + if ( editor.mode ) { + var isDirty = editor.checkDirty(); + + editor._.previousMode = editor.mode; + + editor.fire( 'beforeModeUnload' ); + + // Detach the current editable. + editor.editable( 0 ); + + // Clear up the mode space. + editor.ui.space( 'contents' ).setHtml( '' ); + + editor.mode = ''; + } + + // Fire the mode handler. + this._.modes[ newMode ]( function() { + // Set the current mode. + editor.mode = newMode; + + if ( isDirty !== undefined ) { + !isDirty && editor.resetDirty(); + } + + // Delay to avoid race conditions (setMode inside setMode). + setTimeout( function() { + editor.fire( 'mode' ); + callback && callback.call( editor ); + }, 0); + }); + }; + + /** + * Resizes the editor interface. + * + * editor.resize( 900, 300 ); + * + * editor.resize( '100%', 450, true ); + * + * @param {Number/String} width The new width. It can be an pixels integer or a + * CSS size value. + * @param {Number/String} height The new height. It can be an pixels integer or + * a CSS size value. + * @param {Boolean} [isContentHeight] Indicates that the provided height is to + * be applied to the editor contents space, not to the entire editor + * interface. Defaults to false. + * @param {Boolean} [resizeInner] Indicates that the first inner interface + * element must receive the size, not the outer element. The default theme + * defines the interface inside a pair of span elements + * (`<span><span>...</span></span>`). By default the + * first span element receives the sizes. If this parameter is set to + * true, the second span is sized instead. + */ + CKEDITOR.editor.prototype.resize = function( width, height, isContentHeight, resizeInner ) { + var container = this.container, + contents = this.ui.space( 'contents' ), + contentsFrame = CKEDITOR.env.webkit && this.document && this.document.getWindow().$.frameElement, + outer = resizeInner ? container.getChild( 1 ) : container; + + // Set as border box width. (#5353) + outer.setSize( 'width', width, true ); + + // WebKit needs to refresh the iframe size to avoid rendering issues. (1/2) (#8348) + contentsFrame && ( contentsFrame.style.width = '1%' ); + + // Get the height delta between the outer table and the content area. + // If we're setting the content area's height, then we don't need the delta. + var delta = isContentHeight ? 0 : ( outer.$.offsetHeight || 0 ) - ( contents.$.clientHeight || 0 ); + contents.setStyle( 'height', Math.max( height - delta, 0 ) + 'px' ); + + // WebKit needs to refresh the iframe size to avoid rendering issues. (2/2) (#8348) + contentsFrame && ( contentsFrame.style.width = '100%' ); + + // Emit a resize event. + this.fire( 'resize' ); + }; + + /** + * Gets the element that can be freely used to check the editor size. This method + * is mainly used by the resize plugin, which adds a UI handle that can be used + * to resize the editor. + * + * @param {Boolean} forContents Whether to return the "contents" part of the theme instead of the container. + * @returns {CKEDITOR.dom.element} The resizable element. + */ + CKEDITOR.editor.prototype.getResizable = function( forContents ) { + return forContents ? this.ui.space( 'contents' ) : this.container; + }; + + function createInstance( element, config, data, mode ) { + if ( !CKEDITOR.env.isCompatible ) + return null; + + element = CKEDITOR.dom.element.get( element ); + + // Avoid multiple inline editor instances on the same element. + if ( element.getEditor() ) + throw 'The editor instance "' + element.getEditor().name + '" is already attached to the provided element.'; + + // Create the editor instance. + var editor = new CKEDITOR.editor( config, element, mode ); + + // Do not replace the textarea right now, just hide it. The effective + // replacement will be done later in the editor creation lifecycle. + if ( mode == CKEDITOR.ELEMENT_MODE_REPLACE ) + element.setStyle( 'visibility', 'hidden' ); + + data && editor.setData( data, null, true ); + + // Once the editor is loaded, start the UI. + editor.on( 'loaded', function() { + loadTheme( editor ); + + if ( mode == CKEDITOR.ELEMENT_MODE_REPLACE && editor.config.autoUpdateElement ) + attachToForm( editor ); + + editor.setMode( editor.config.startupMode, function() { + // Clean on startup. + editor.resetDirty(); + + // Editor is completely loaded for interaction. + editor.status = 'ready'; + editor.fireOnce( 'instanceReady' ); + CKEDITOR.fire( 'instanceReady', null, editor ); + }); + }); + + editor.on( 'destroy', destroy ); + return editor; + } + + function destroy() { + var editor = this, + container = editor.container, + element = editor.element; + + if ( container ) { + container.clearCustomData(); + container.remove(); + } + + if ( element ) { + element.clearCustomData(); + editor.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE && element.show(); + delete editor.element; + } + } + + var themedTpl; + + function loadTheme( editor ) { + var name = editor.name, + element = editor.element, + elementMode = editor.elementMode; + + // Get the HTML for the predefined spaces. + var topHtml = editor.fire( 'uiSpace', { space: 'top', html: '' } ).html; + var bottomHtml = editor.fire( 'uiSpace', { space: 'bottom', html: '' } ).html; + + if ( !themedTpl ) { + themedTpl = CKEDITOR.addTemplate( 'maincontainer', '<{outerEl}' + + ' id="cke_{name}"' + + ' class="{id} cke cke_reset cke_chrome cke_editor_{name} cke_{langDir} ' + CKEDITOR.env.cssClass + '" ' + + ' dir="{langDir}"' + + ' lang="{langCode}"' + + ' role="application"' + + ' aria-labelledby="cke_{name}_arialbl">' + + '<span id="cke_{name}_arialbl" class="cke_voice_label">{voiceLabel}</span>' + + '<{outerEl} class="cke_inner cke_reset" role="presentation">' + + '{topHtml}' + + '<{outerEl} id="{contentId}" class="cke_contents cke_reset" role="presentation"></{outerEl}>' + + '{bottomHtml}' + + '</{outerEl}>' + + '</{outerEl}>' ); + } + + var container = CKEDITOR.dom.element.createFromHtml( themedTpl.output({ + id: editor.id, + name: name, + langDir: editor.lang.dir, + langCode: editor.langCode, + voiceLabel: [ editor.lang.editor, editor.name ].join( ', ' ), + topHtml: topHtml ? '<span id="' + editor.ui.spaceId( 'top' ) + '" class="cke_top cke_reset_all" role="presentation" style="height:auto">' + topHtml + '</span>' : '', + contentId: editor.ui.spaceId( 'contents' ), + bottomHtml: bottomHtml ? '<span id="' + editor.ui.spaceId( 'bottom' ) + '" class="cke_bottom cke_reset_all" role="presentation">' + bottomHtml + '</span>' : '', + outerEl: CKEDITOR.env.ie ? 'span' : 'div' // #9571 + })); + + if ( elementMode == CKEDITOR.ELEMENT_MODE_REPLACE ) { + element.hide(); + container.insertAfter( element ); + } else + element.append( container ); + + editor.container = container; + + // Make top and bottom spaces unelectable, but not content space, + // otherwise the editable area would be affected. + topHtml && editor.ui.space( 'top' ).unselectable(); + bottomHtml && editor.ui.space( 'bottom' ).unselectable(); + + var width = editor.config.width, height = editor.config.height; + if ( width ) + container.setStyle( 'width', CKEDITOR.tools.cssLength( width ) ); + + // The editor height is applied to the contents space. + if ( height ) + editor.ui.space( 'contents' ).setStyle( 'height', CKEDITOR.tools.cssLength( height ) ); + + // Disable browser context menu for editor's chrome. + container.disableContextMenu(); + + // Redirect the focus into editor for webkit. (#5713) + CKEDITOR.env.webkit && container.on( 'focus', function() { + editor.focus(); + }); + + editor.fireOnce( 'uiReady' ); + } + + function attachToForm( editor ) { + var element = editor.element; + + // If are replacing a textarea, we must + if ( editor.elementMode == CKEDITOR.ELEMENT_MODE_REPLACE && element.is( 'textarea' ) ) { + var form = element.$.form && new CKEDITOR.dom.element( element.$.form ); + if ( form ) { + function onSubmit() { + editor.updateElement(); + } + form.on( 'submit', onSubmit ); + + // Setup the submit function because it doesn't fire the + // "submit" event. + if ( !form.$.submit.nodeName && !form.$.submit.length ) { + form.$.submit = CKEDITOR.tools.override( form.$.submit, function( originalSubmit ) { + return function() { + editor.updateElement(); + + // For IE, the DOM submit function is not a + // function, so we need third check. + if ( originalSubmit.apply ) + originalSubmit.apply( this, arguments ); + else + originalSubmit(); + }; + }); + } + + // Remove 'submit' events registered on form element before destroying.(#3988) + editor.on( 'destroy', function() { + form.removeListener( 'submit', onSubmit ); + }); + } + } + } + + // Replace all textareas with the default class name. + CKEDITOR.domReady( function() { + CKEDITOR.replaceClass && CKEDITOR.replaceAll( CKEDITOR.replaceClass ); + }); +})(); + +/** + * The current editing mode. An editing mode basically provides + * different ways of editing or viewing the contents. + * + * alert( CKEDITOR.instances.editor1.mode ); // (e.g.) 'wysiwyg' + * + * @readonly + * @property {String} mode + */ + +/** + * The mode to load at the editor startup. It depends on the plugins + * loaded. By default, the `wysiwyg` and `source` modes are available. + * + * config.startupMode = 'source'; + * + * @cfg {String} [startupMode='wysiwyg'] + * @member CKEDITOR.config + */ +CKEDITOR.config.startupMode = 'wysiwyg'; + +/** + * Fired after the editor instance is resized through + * the {@link CKEDITOR.editor#method-resize} method. + * + * @event resize + */ + +/** + * Event fired before changing the editing mode. See also + * {@link #beforeSetMode} and {@link #event-mode}. + * + * @event beforeModeUnload + */ + +/** + * Event fired before the editor mode is set. See also + * {@link #event-mode} and {@link #beforeModeUnload}. + * + * @since 3.5.3 + * @event beforeSetMode + * @param {String} data The name of the mode which is about to be set. + */ + +/** + * Fired after setting the editing mode. See also {@link #beforeSetMode} and {@link #beforeModeUnload} + * + * @event mode + */ |
