/* Copyright (c) 2003-2012, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ /** * @fileOverview The floating dialog plugin. */ /** * No resize for this dialog. * @constant */ CKEDITOR.DIALOG_RESIZE_NONE = 0; /** * Only allow horizontal resizing for this dialog, disable vertical resizing. * @constant */ CKEDITOR.DIALOG_RESIZE_WIDTH = 1; /** * Only allow vertical resizing for this dialog, disable horizontal resizing. * @constant */ CKEDITOR.DIALOG_RESIZE_HEIGHT = 2; /* * Allow the dialog to be resized in both directions. * @constant */ CKEDITOR.DIALOG_RESIZE_BOTH = 3; (function() { var cssLength = CKEDITOR.tools.cssLength; function isTabVisible( tabId ) { return !!this._.tabs[ tabId ][ 0 ].$.offsetHeight; } function getPreviousVisibleTab() { var tabId = this._.currentTabId, length = this._.tabIdList.length, tabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, tabId ) + length; for ( var i = tabIndex - 1 ; i > tabIndex - length ; i-- ) { if ( isTabVisible.call( this, this._.tabIdList[ i % length ] ) ) return this._.tabIdList[ i % length ]; } return null; } function getNextVisibleTab() { var tabId = this._.currentTabId, length = this._.tabIdList.length, tabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, tabId ); for ( var i = tabIndex + 1 ; i < tabIndex + length ; i++ ) { if ( isTabVisible.call( this, this._.tabIdList[ i % length ] ) ) return this._.tabIdList[ i % length ]; } return null; } function clearOrRecoverTextInputValue( container, isRecover ) { var inputs = container.$.getElementsByTagName( 'input' ); for ( var i = 0, length = inputs.length; i < length ; i++ ) { var item = new CKEDITOR.dom.element( inputs[ i ] ); if ( item.getAttribute( 'type' ).toLowerCase() == 'text' ) { if ( isRecover ) { item.setAttribute( 'value', item.getCustomData( 'fake_value' ) || '' ); item.removeCustomData( 'fake_value' ); } else { item.setCustomData( 'fake_value', item.getAttribute( 'value' ) ); item.setAttribute( 'value', '' ); } } } } // Handle dialog element validation state UI changes. function handleFieldValidated( isValid, msg ) { var input = this.getInputElement(); if ( input ) { isValid ? input.removeAttribute( 'aria-invalid' ) : input.setAttribute( 'aria-invalid', true ); } if ( !isValid ) { if ( this.select ) this.select(); else this.focus(); } msg && alert( msg ); this.fire( 'validated', { valid : isValid, msg : msg } ); } function resetField() { var input = this.getInputElement(); input && input.removeAttribute( 'aria-invalid' ); } /** * This is the base class for runtime dialog objects. An instance of this * class represents a single named dialog for a single editor instance. * @param {Object} editor The editor which created the dialog. * @param {String} dialogName The dialog's registered name. * @constructor * @example * var dialogObj = new CKEDITOR.dialog( editor, 'smiley' ); */ CKEDITOR.dialog = function( editor, dialogName ) { // Load the dialog definition. var definition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ], defaultDefinition = CKEDITOR.tools.clone( defaultDialogDefinition ), buttonsOrder = editor.config.dialog_buttonsOrder || 'OS', dir = editor.lang.dir, tabsToRemove = {}, i, processed, stopPropagation; if ( ( buttonsOrder == 'OS' && CKEDITOR.env.mac ) || // The buttons in MacOS Apps are in reverse order (#4750) ( buttonsOrder == 'rtl' && dir == 'ltr' ) || ( buttonsOrder == 'ltr' && dir == 'rtl' ) ) defaultDefinition.buttons.reverse(); // Completes the definition with the default values. definition = CKEDITOR.tools.extend( definition( editor ), defaultDefinition ); // Clone a functionally independent copy for this dialog. definition = CKEDITOR.tools.clone( definition ); // Create a complex definition object, extending it with the API // functions. definition = new definitionObject( this, definition ); var doc = CKEDITOR.document; var themeBuilt = editor.theme.buildDialog( editor ); // Initialize some basic parameters. this._ = { editor : editor, element : themeBuilt.element, name : dialogName, contentSize : { width : 0, height : 0 }, size : { width : 0, height : 0 }, contents : {}, buttons : {}, accessKeyMap : {}, // Initialize the tab and page map. tabs : {}, tabIdList : [], currentTabId : null, currentTabIndex : null, pageCount : 0, lastTab : null, tabBarMode : false, // Initialize the tab order array for input widgets. focusList : [], currentFocusIndex : 0, hasFocus : false }; this.parts = themeBuilt.parts; CKEDITOR.tools.setTimeout( function() { editor.fire( 'ariaWidget', this.parts.contents ); }, 0, this ); // Set the startup styles for the dialog, avoiding it enlarging the // page size on the dialog creation. var startStyles = { position : CKEDITOR.env.ie6Compat ? 'absolute' : 'fixed', top : 0, visibility : 'hidden' }; startStyles[ dir == 'rtl' ? 'right' : 'left' ] = 0; this.parts.dialog.setStyles( startStyles ); // Call the CKEDITOR.event constructor to initialize this instance. CKEDITOR.event.call( this ); // Fire the "dialogDefinition" event, making it possible to customize // the dialog definition. this.definition = definition = CKEDITOR.fire( 'dialogDefinition', { name : dialogName, definition : definition } , editor ).definition; // Cache tabs that should be removed. if ( !( 'removeDialogTabs' in editor._ ) && editor.config.removeDialogTabs ) { var removeContents = editor.config.removeDialogTabs.split( ';' ); for ( i = 0; i < removeContents.length; i++ ) { var parts = removeContents[ i ].split( ':' ); if ( parts.length == 2 ) { var removeDialogName = parts[ 0 ]; if ( !tabsToRemove[ removeDialogName ] ) tabsToRemove[ removeDialogName ] = []; tabsToRemove[ removeDialogName ].push( parts[ 1 ] ); } } editor._.removeDialogTabs = tabsToRemove; } // Remove tabs of this dialog. if ( editor._.removeDialogTabs && ( tabsToRemove = editor._.removeDialogTabs[ dialogName ] ) ) { for ( i = 0; i < tabsToRemove.length; i++ ) definition.removeContents( tabsToRemove[ i ] ); } // Initialize load, show, hide, ok and cancel events. if ( definition.onLoad ) this.on( 'load', definition.onLoad ); if ( definition.onShow ) this.on( 'show', definition.onShow ); if ( definition.onHide ) this.on( 'hide', definition.onHide ); if ( definition.onOk ) { this.on( 'ok', function( evt ) { // Dialog confirm might probably introduce content changes (#5415). editor.fire( 'saveSnapshot' ); setTimeout( function () { editor.fire( 'saveSnapshot' ); }, 0 ); if ( definition.onOk.call( this, evt ) === false ) evt.data.hide = false; }); } if ( definition.onCancel ) { this.on( 'cancel', function( evt ) { if ( definition.onCancel.call( this, evt ) === false ) evt.data.hide = false; }); } var me = this; // Iterates over all items inside all content in the dialog, calling a // function for each of them. var iterContents = function( func ) { var contents = me._.contents, stop = false; for ( var i in contents ) { for ( var j in contents[i] ) { stop = func.call( this, contents[i][j] ); if ( stop ) return; } } }; this.on( 'ok', function( evt ) { iterContents( function( item ) { if ( item.validate ) { var retval = item.validate( this ), invalid = typeof ( retval ) == 'string' || retval === false; if ( invalid ) { evt.data.hide = false; evt.stop(); } handleFieldValidated.call( item, !invalid, typeof retval == 'string' ? retval : undefined ); return invalid; } }); }, this, null, 0 ); this.on( 'cancel', function( evt ) { iterContents( function( item ) { if ( item.isChanged() ) { if ( !confirm( editor.lang.common.confirmCancel ) ) evt.data.hide = false; return true; } }); }, this, null, 0 ); this.parts.close.on( 'click', function( evt ) { if ( this.fire( 'cancel', { hide : true } ).hide !== false ) this.hide(); evt.data.preventDefault(); }, this ); // Sort focus list according to tab order definitions. function setupFocus() { var focusList = me._.focusList; focusList.sort( function( a, b ) { // Mimics browser tab order logics; if ( a.tabIndex != b.tabIndex ) return b.tabIndex - a.tabIndex; // Sort is not stable in some browsers, // fall-back the comparator to 'focusIndex'; else return a.focusIndex - b.focusIndex; }); var size = focusList.length; for ( var i = 0; i < size; i++ ) focusList[ i ].focusIndex = i; } function changeFocus( offset ) { var focusList = me._.focusList; offset = offset || 0; if ( focusList.length < 1 ) return; var current = me._.currentFocusIndex; // Trigger the 'blur' event of any input element before anything, // since certain UI updates may depend on it. try { focusList[ current ].getInputElement().$.blur(); } catch( e ){} var startIndex = ( current + offset + focusList.length ) % focusList.length, currentIndex = startIndex; while ( offset && !focusList[ currentIndex ].isFocusable() ) { currentIndex = ( currentIndex + offset + focusList.length ) % focusList.length; if ( currentIndex == startIndex ) break; } focusList[ currentIndex ].focus(); // Select whole field content. if ( focusList[ currentIndex ].type == 'text' ) focusList[ currentIndex ].select(); } this.changeFocus = changeFocus; function keydownHandler( evt ) { // If I'm not the top dialog, ignore. if ( me != CKEDITOR.dialog._.currentTop ) return; var keystroke = evt.data.getKeystroke(), rtl = editor.lang.dir == 'rtl', button; processed = stopPropagation = 0; if ( keystroke == 9 || keystroke == CKEDITOR.SHIFT + 9 ) { var shiftPressed = ( keystroke == CKEDITOR.SHIFT + 9 ); // Handling Tab and Shift-Tab. if ( me._.tabBarMode ) { // Change tabs. var nextId = shiftPressed ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ); me.selectPage( nextId ); me._.tabs[ nextId ][ 0 ].focus(); } else { // Change the focus of inputs. changeFocus( shiftPressed ? -1 : 1 ); } processed = 1; } else if ( keystroke == CKEDITOR.ALT + 121 && !me._.tabBarMode && me.getPageCount() > 1 ) { // Alt-F10 puts focus into the current tab item in the tab bar. me._.tabBarMode = true; me._.tabs[ me._.currentTabId ][ 0 ].focus(); processed = 1; } else if ( ( keystroke == 37 || keystroke == 39 ) && me._.tabBarMode ) { // Arrow keys - used for changing tabs. nextId = ( keystroke == ( rtl ? 39 : 37 ) ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ) ); me.selectPage( nextId ); me._.tabs[ nextId ][ 0 ].focus(); processed = 1; } else if ( ( keystroke == 13 || keystroke == 32 ) && me._.tabBarMode ) { this.selectPage( this._.currentTabId ); this._.tabBarMode = false; this._.currentFocusIndex = -1; changeFocus( 1 ); processed = 1; } // If user presses enter key in a text box, it implies clicking OK for the dialog. else if ( keystroke == 13 /*ENTER*/ ) { // Don't do that for a target that handles ENTER. var target = evt.data.getTarget(); if ( !target.is( 'a', 'button', 'select' ) && ( !target.is( 'input' ) || target.$.type != 'button' ) ) { button = this.getButton( 'ok' ); button && CKEDITOR.tools.setTimeout( button.click, 0, button ); processed = 1; } stopPropagation = 1; // Always block the propagation (#4269) } else if ( keystroke == 27 /*ESC*/ ) { button = this.getButton( 'cancel' ); // If there's a Cancel button, click it, else just fire the cancel event and hide the dialog. if ( button ) CKEDITOR.tools.setTimeout( button.click, 0, button ); else { if ( this.fire( 'cancel', { hide : true } ).hide !== false ) this.hide(); } stopPropagation = 1; // Always block the propagation (#4269) } else return; keypressHandler( evt ); } function keypressHandler( evt ) { if ( processed ) evt.data.preventDefault(1); else if ( stopPropagation ) evt.data.stopPropagation(); } var dialogElement = this._.element; // Add the dialog keyboard handlers. this.on( 'show', function() { dialogElement.on( 'keydown', keydownHandler, this ); // Some browsers instead, don't cancel key events in the keydown, but in the // keypress. So we must do a longer trip in those cases. (#4531,#8985) if ( CKEDITOR.env.opera || CKEDITOR.env.gecko ) dialogElement.on( 'keypress', keypressHandler, this ); } ); this.on( 'hide', function() { dialogElement.removeListener( 'keydown', keydownHandler ); if ( CKEDITOR.env.opera || CKEDITOR.env.gecko ) dialogElement.removeListener( 'keypress', keypressHandler ); // Reset fields state when closing dialog. iterContents( function( item ) { resetField.apply( item ); } ); } ); this.on( 'iframeAdded', function( evt ) { var doc = new CKEDITOR.dom.document( evt.data.iframe.$.contentWindow.document ); doc.on( 'keydown', keydownHandler, this, null, 0 ); } ); // Auto-focus logic in dialog. this.on( 'show', function() { // Setup tabIndex on showing the dialog instead of on loading // to allow dynamic tab order happen in dialog definition. setupFocus(); if ( editor.config.dialog_startupFocusTab && me._.pageCount > 1 ) { me._.tabBarMode = true; me._.tabs[ me._.currentTabId ][ 0 ].focus(); } else if ( !this._.hasFocus ) { this._.currentFocusIndex = -1; // Decide where to put the initial focus. if ( definition.onFocus ) { var initialFocus = definition.onFocus.call( this ); // Focus the field that the user specified. initialFocus && initialFocus.focus(); } // Focus the first field in layout order. else changeFocus( 1 ); /* * IE BUG: If the initial focus went into a non-text element (e.g. button), * then IE would still leave the caret inside the editing area. */ if ( this._.editor.mode == 'wysiwyg' && CKEDITOR.env.ie ) { var $selection = editor.document.$.selection, $range = $selection.createRange(); if ( $range ) { if ( $range.parentElement && $range.parentElement().ownerDocument == editor.document.$ || $range.item && $range.item( 0 ).ownerDocument == editor.document.$ ) { var $myRange = document.body.createTextRange(); $myRange.moveToElementText( this.getElement().getFirst().$ ); $myRange.collapse( true ); $myRange.select(); } } } } }, this, null, 0xffffffff ); // IE6 BUG: Text fields and text areas are only half-rendered the first time the dialog appears in IE6 (#2661). // This is still needed after [2708] and [2709] because text fields in hidden TR tags are still broken. if ( CKEDITOR.env.ie6Compat ) { this.on( 'load', function( evt ) { var outer = this.getElement(), inner = outer.getFirst(); inner.remove(); inner.appendTo( outer ); }, this ); } initDragAndDrop( this ); initResizeHandles( this ); // Insert the title. ( new CKEDITOR.dom.text( definition.title, CKEDITOR.document ) ).appendTo( this.parts.title ); // Insert the tabs and contents. for ( i = 0 ; i < definition.contents.length ; i++ ) { var page = definition.contents[i]; page && this.addPage( page ); } this.parts[ 'tabs' ].on( 'click', function( evt ) { var target = evt.data.getTarget(); // If we aren't inside a tab, bail out. if ( target.hasClass( 'cke_dialog_tab' ) ) { // Get the ID of the tab, without the 'cke_' prefix and the unique number suffix. var id = target.$.id; this.selectPage( id.substring( 4, id.lastIndexOf( '_' ) ) ); if ( this._.tabBarMode ) { this._.tabBarMode = false; this._.currentFocusIndex = -1; changeFocus( 1 ); } evt.data.preventDefault(); } }, this ); // Insert buttons. var buttonsHtml = [], buttons = CKEDITOR.dialog._.uiElementBuilders.hbox.build( this, { type : 'hbox', className : 'cke_dialog_footer_buttons', widths : [], children : definition.buttons }, buttonsHtml ).getChild(); this.parts.footer.setHtml( buttonsHtml.join( '' ) ); for ( i = 0 ; i < buttons.length ; i++ ) this._.buttons[ buttons[i].id ] = buttons[i]; }; // Focusable interface. Use it via dialog.addFocusable. function Focusable( dialog, element, index ) { this.element = element; this.focusIndex = index; // TODO: support tabIndex for focusables. this.tabIndex = 0; this.isFocusable = function() { return !element.getAttribute( 'disabled' ) && element.isVisible(); }; this.focus = function() { dialog._.currentFocusIndex = this.focusIndex; this.element.focus(); }; // Bind events element.on( 'keydown', function( e ) { if ( e.data.getKeystroke() in { 32:1, 13:1 } ) this.fire( 'click' ); } ); element.on( 'focus', function() { this.fire( 'mouseover' ); } ); element.on( 'blur', function() { this.fire( 'mouseout' ); } ); } CKEDITOR.dialog.prototype = { destroy : function() { this.hide(); this._.element.remove(); }, /** * Resizes the dialog. * @param {Number} width The width of the dialog in pixels. * @param {Number} height The height of the dialog in pixels. * @function * @example * dialogObj.resize( 800, 640 ); */ resize : (function() { return function( width, height ) { if ( this._.contentSize && this._.contentSize.width == width && this._.contentSize.height == height ) return; CKEDITOR.dialog.fire( 'resize', { dialog : this, skin : this._.editor.skinName, width : width, height : height }, this._.editor ); this.fire( 'resize', { skin : this._.editor.skinName, width : width, height : height }, this._.editor ); // Update dialog position when dimension get changed in RTL. if ( this._.editor.lang.dir == 'rtl' && this._.position ) this._.position.x = CKEDITOR.document.getWindow().getViewPaneSize().width - this._.contentSize.width - parseInt( this._.element.getFirst().getStyle( 'right' ), 10 ); this._.contentSize = { width : width, height : height }; }; })(), /** * Gets the current size of the dialog in pixels. * @returns {Object} An object with "width" and "height" properties. * @example * var width = dialogObj.getSize().width; */ getSize : function() { var element = this._.element.getFirst(); return { width : element.$.offsetWidth || 0, height : element.$.offsetHeight || 0}; }, /** * Moves the dialog to an (x, y) coordinate relative to the window. * @function * @param {Number} x The target x-coordinate. * @param {Number} y The target y-coordinate. * @param {Boolean} save Flag indicate whether the dialog position should be remembered on next open up. * @example * dialogObj.move( 10, 40 ); */ move : (function() { var isFixed; return function( x, y, save ) { // The dialog may be fixed positioned or absolute positioned. Ask the // browser what is the current situation first. var element = this._.element.getFirst(), rtl = this._.editor.lang.dir == 'rtl'; if ( isFixed === undefined ) isFixed = element.getComputedStyle( 'position' ) == 'fixed'; if ( isFixed && this._.position && this._.position.x == x && this._.position.y == y ) return; // Save the current position. this._.position = { x : x, y : y }; // If not fixed positioned, add scroll position to the coordinates. if ( !isFixed ) { var scrollPosition = CKEDITOR.document.getWindow().getScrollPosition(); x += scrollPosition.x; y += scrollPosition.y; } // Translate coordinate for RTL. if ( rtl ) { var dialogSize = this.getSize(), viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(); x = viewPaneSize.width - dialogSize.width - x; } var styles = { 'top' : ( y > 0 ? y : 0 ) + 'px' }; styles[ rtl ? 'right' : 'left' ] = ( x > 0 ? x : 0 ) + 'px'; element.setStyles( styles ); save && ( this._.moved = 1 ); }; })(), /** * Gets the dialog's position in the window. * @returns {Object} An object with "x" and "y" properties. * @example * var dialogX = dialogObj.getPosition().x; */ getPosition : function(){ return CKEDITOR.tools.extend( {}, this._.position ); }, /** * Shows the dialog box. * @example * dialogObj.show(); */ show : function() { // Insert the dialog's element to the root document. var element = this._.element; var definition = this.definition; if ( !( element.getParent() && element.getParent().equals( CKEDITOR.document.getBody() ) ) ) element.appendTo( CKEDITOR.document.getBody() ); else element.setStyle( 'display', 'block' ); // FIREFOX BUG: Fix vanishing caret for Firefox 2 or Gecko 1.8. if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 ) { var dialogElement = this.parts.dialog; dialogElement.setStyle( 'position', 'absolute' ); setTimeout( function() { dialogElement.setStyle( 'position', 'fixed' ); }, 0 ); } // First, set the dialog to an appropriate size. this.resize( this._.contentSize && this._.contentSize.width || definition.width || definition.minWidth, this._.contentSize && this._.contentSize.height || definition.height || definition.minHeight ); // Reset all inputs back to their default value. this.reset(); // Select the first tab by default. this.selectPage( this.definition.contents[0].id ); // Set z-index. if ( CKEDITOR.dialog._.currentZIndex === null ) CKEDITOR.dialog._.currentZIndex = this._.editor.config.baseFloatZIndex; this._.element.getFirst().setStyle( 'z-index', CKEDITOR.dialog._.currentZIndex += 10 ); // Maintain the dialog ordering and dialog cover. if ( CKEDITOR.dialog._.currentTop === null ) { CKEDITOR.dialog._.currentTop = this; this._.parentDialog = null; showCover( this._.editor ); } else { this._.parentDialog = CKEDITOR.dialog._.currentTop; var parentElement = this._.parentDialog.getElement().getFirst(); parentElement.$.style.zIndex -= Math.floor( this._.editor.config.baseFloatZIndex / 2 ); CKEDITOR.dialog._.currentTop = this; } element.on( 'keydown', accessKeyDownHandler ); element.on( CKEDITOR.env.opera ? 'keypress' : 'keyup', accessKeyUpHandler ); // Reset the hasFocus state. this._.hasFocus = false; CKEDITOR.tools.setTimeout( function() { this.layout(); this.parts.dialog.setStyle( 'visibility', '' ); // Execute onLoad for the first show. this.fireOnce( 'load', {} ); CKEDITOR.ui.fire( 'ready', this ); this.fire( 'show', {} ); this._.editor.fire( 'dialogShow', this ); // Save the initial values of the dialog. this.foreach( function( contentObj ) { contentObj.setInitValue && contentObj.setInitValue(); } ); }, 100, this ); }, /** * Rearrange the dialog to its previous position or the middle of the window. * @since 3.5 */ layout : function() { var viewSize = CKEDITOR.document.getWindow().getViewPaneSize(), dialogSize = this.getSize(); this.move( this._.moved ? this._.position.x : ( viewSize.width - dialogSize.width ) / 2, this._.moved ? this._.position.y : ( viewSize.height - dialogSize.height ) / 2 ); }, /** * Executes a function for each UI element. * @param {Function} fn Function to execute for each UI element. * @returns {CKEDITOR.dialog} The current dialog object. */ foreach : function( fn ) { for ( var i in this._.contents ) { for ( var j in this._.contents[i] ) fn.call( this, this._.contents[i][j] ); } return this; }, /** * Resets all input values in the dialog. * @example * dialogObj.reset(); * @returns {CKEDITOR.dialog} The current dialog object. */ reset : (function() { var fn = function( widget ){ if ( widget.reset ) widget.reset( 1 ); }; return function(){ this.foreach( fn ); return this; }; })(), /** * Calls the {@link CKEDITOR.dialog.definition.uiElement#setup} method of each of the UI elements, with the arguments passed through it. * It is usually being called when the dialog is opened, to put the initial value inside the field. * @example * dialogObj.setupContent(); * @example * var timestamp = ( new Date() ).valueOf(); * dialogObj.setupContent( timestamp ); */ setupContent : function() { var args = arguments; this.foreach( function( widget ) { if ( widget.setup ) widget.setup.apply( widget, args ); }); }, /** * Calls the {@link CKEDITOR.dialog.definition.uiElement#commit} method of each of the UI elements, with the arguments passed through it. * It is usually being called when the user confirms the dialog, to process the values. * @example * dialogObj.commitContent(); * @example * var timestamp = ( new Date() ).valueOf(); * dialogObj.commitContent( timestamp ); */ commitContent : function() { var args = arguments; this.foreach( function( widget ) { // Make sure IE triggers "change" event on last focused input before closing the dialog. (#7915) if ( CKEDITOR.env.ie && this._.currentFocusIndex == widget.focusIndex ) widget.getInputElement().$.blur(); if ( widget.commit ) widget.commit.apply( widget, args ); }); }, /** * Hides the dialog box. * @example * dialogObj.hide(); */ hide : function() { if ( !this.parts.dialog.isVisible() ) return; this.fire( 'hide', {} ); this._.editor.fire( 'dialogHide', this ); // Reset the tab page. this.selectPage( this._.tabIdList[ 0 ] ); var element = this._.element; element.setStyle( 'display', 'none' ); this.parts.dialog.setStyle( 'visibility', 'hidden' ); // Unregister all access keys associated with this dialog. unregisterAccessKey( this ); // Close any child(top) dialogs first. while( CKEDITOR.dialog._.currentTop != this ) CKEDITOR.dialog._.currentTop.hide(); // Maintain dialog ordering and remove cover if needed. if ( !this._.parentDialog ) hideCover(); else { var parentElement = this._.parentDialog.getElement().getFirst(); parentElement.setStyle( 'z-index', parseInt( parentElement.$.style.zIndex, 10 ) + Math.floor( this._.editor.config.baseFloatZIndex / 2 ) ); } CKEDITOR.dialog._.currentTop = this._.parentDialog; // Deduct or clear the z-index. if ( !this._.parentDialog ) { CKEDITOR.dialog._.currentZIndex = null; // Remove access key handlers. element.removeListener( 'keydown', accessKeyDownHandler ); element.removeListener( CKEDITOR.env.opera ? 'keypress' : 'keyup', accessKeyUpHandler ); var editor = this._.editor; editor.focus(); if ( editor.mode == 'wysiwyg' && CKEDITOR.env.ie ) { var selection = editor.getSelection(); selection && selection.unlock( true ); } } else CKEDITOR.dialog._.currentZIndex -= 10; delete this._.parentDialog; // Reset the initial values of the dialog. this.foreach( function( contentObj ) { contentObj.resetInitValue && contentObj.resetInitValue(); } ); }, /** * Adds a tabbed page into the dialog. * @param {Object} contents Content definition. * @example */ addPage : function( contents ) { var pageHtml = [], titleHtml = contents.label ? ' title="' + CKEDITOR.tools.htmlEncode( contents.label ) + '"' : '', elements = contents.elements, vbox = CKEDITOR.dialog._.uiElementBuilders.vbox.build( this, { type : 'vbox', className : 'cke_dialog_page_contents', children : contents.elements, expand : !!contents.expand, padding : contents.padding, style : contents.style || 'width: 100%;height:100%' }, pageHtml ); // Create the HTML for the tab and the content block. var page = CKEDITOR.dom.element.createFromHtml( pageHtml.join( '' ) ); page.setAttribute( 'role', 'tabpanel' ); var env = CKEDITOR.env; var tabId = 'cke_' + contents.id + '_' + CKEDITOR.tools.getNextNumber(), tab = CKEDITOR.dom.element.createFromHtml( [ ' 0 ? ' cke_last' : 'cke_first' ), titleHtml, ( !!contents.hidden ? ' style="display:none"' : '' ), ' id="', tabId, '"', env.gecko && env.version >= 10900 && !env.hc ? '' : ' href="javascript:void(0)"', ' tabIndex="-1"', ' hidefocus="true"', ' role="tab">', contents.label, '' ].join( '' ) ); page.setAttribute( 'aria-labelledby', tabId ); // Take records for the tabs and elements created. this._.tabs[ contents.id ] = [ tab, page ]; this._.tabIdList.push( contents.id ); !contents.hidden && this._.pageCount++; this._.lastTab = tab; this.updateStyle(); var contentMap = this._.contents[ contents.id ] = {}, cursor, children = vbox.getChild(); while ( ( cursor = children.shift() ) ) { contentMap[ cursor.id ] = cursor; if ( typeof( cursor.getChild ) == 'function' ) children.push.apply( children, cursor.getChild() ); } // Attach the DOM nodes. page.setAttribute( 'name', contents.id ); page.appendTo( this.parts.contents ); tab.unselectable(); this.parts.tabs.append( tab ); // Add access key handlers if access key is defined. if ( contents.accessKey ) { registerAccessKey( this, this, 'CTRL+' + contents.accessKey, tabAccessKeyDown, tabAccessKeyUp ); this._.accessKeyMap[ 'CTRL+' + contents.accessKey ] = contents.id; } }, /** * Activates a tab page in the dialog by its id. * @param {String} id The id of the dialog tab to be activated. * @example * dialogObj.selectPage( 'tab_1' ); */ selectPage : function( id ) { if ( this._.currentTabId == id ) return; // Returning true means that the event has been canceled if ( this.fire( 'selectPage', { page : id, currentPage : this._.currentTabId } ) === true ) return; // Hide the non-selected tabs and pages. for ( var i in this._.tabs ) { var tab = this._.tabs[i][0], page = this._.tabs[i][1]; if ( i != id ) { tab.removeClass( 'cke_dialog_tab_selected' ); page.hide(); } page.setAttribute( 'aria-hidden', i != id ); } var selected = this._.tabs[ id ]; selected[ 0 ].addClass( 'cke_dialog_tab_selected' ); // [IE] an invisible input[type='text'] will enlarge it's width // if it's value is long when it shows, so we clear it's value // before it shows and then recover it (#5649) if ( CKEDITOR.env.ie6Compat || CKEDITOR.env.ie7Compat ) { clearOrRecoverTextInputValue( selected[ 1 ] ); selected[ 1 ].show(); setTimeout( function() { clearOrRecoverTextInputValue( selected[ 1 ], 1 ); }, 0 ); } else selected[ 1 ].show(); this._.currentTabId = id; this._.currentTabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, id ); }, // Dialog state-specific style updates. updateStyle : function() { // If only a single page shown, a different style is used in the central pane. this.parts.dialog[ ( this._.pageCount === 1 ? 'add' : 'remove' ) + 'Class' ]( 'cke_single_page' ); }, /** * Hides a page's tab away from the dialog. * @param {String} id The page's Id. * @example * dialog.hidePage( 'tab_3' ); */ hidePage : function( id ) { var tab = this._.tabs[id] && this._.tabs[id][0]; if ( !tab || this._.pageCount == 1 || !tab.isVisible() ) return; // Switch to other tab first when we're hiding the active tab. else if ( id == this._.currentTabId ) this.selectPage( getPreviousVisibleTab.call( this ) ); tab.hide(); this._.pageCount--; this.updateStyle(); }, /** * Unhides a page's tab. * @param {String} id The page's Id. * @example * dialog.showPage( 'tab_2' ); */ showPage : function( id ) { var tab = this._.tabs[id] && this._.tabs[id][0]; if ( !tab ) return; tab.show(); this._.pageCount++; this.updateStyle(); }, /** * Gets the root DOM element of the dialog. * @returns {CKEDITOR.dom.element} The <span> element containing this dialog. * @example * var dialogElement = dialogObj.getElement().getFirst(); * dialogElement.setStyle( 'padding', '5px' ); */ getElement : function() { return this._.element; }, /** * Gets the name of the dialog. * @returns {String} The name of this dialog. * @example * var dialogName = dialogObj.getName(); */ getName : function() { return this._.name; }, /** * Gets a dialog UI element object from a dialog page. * @param {String} pageId id of dialog page. * @param {String} elementId id of UI element. * @example * dialogObj.getContentElement( 'tabId', 'elementId' ).setValue( 'Example' ); * @returns {CKEDITOR.ui.dialog.uiElement} The dialog UI element. */ getContentElement : function( pageId, elementId ) { var page = this._.contents[ pageId ]; return page && page[ elementId ]; }, /** * Gets the value of a dialog UI element. * @param {String} pageId id of dialog page. * @param {String} elementId id of UI element. * @example * alert( dialogObj.getValueOf( 'tabId', 'elementId' ) ); * @returns {Object} The value of the UI element. */ getValueOf : function( pageId, elementId ) { return this.getContentElement( pageId, elementId ).getValue(); }, /** * Sets the value of a dialog UI element. * @param {String} pageId id of the dialog page. * @param {String} elementId id of the UI element. * @param {Object} value The new value of the UI element. * @example * dialogObj.setValueOf( 'tabId', 'elementId', 'Example' ); */ setValueOf : function( pageId, elementId, value ) { return this.getContentElement( pageId, elementId ).setValue( value ); }, /** * Gets the UI element of a button in the dialog's button row. * @param {String} id The id of the button. * @example * @returns {CKEDITOR.ui.dialog.button} The button object. */ getButton : function( id ) { return this._.buttons[ id ]; }, /** * Simulates a click to a dialog button in the dialog's button row. * @param {String} id The id of the button. * @example * @returns The return value of the dialog's "click" event. */ click : function( id ) { return this._.buttons[ id ].click(); }, /** * Disables a dialog button. * @param {String} id The id of the button. * @example */ disableButton : function( id ) { return this._.buttons[ id ].disable(); }, /** * Enables a dialog button. * @param {String} id The id of the button. * @example */ enableButton : function( id ) { return this._.buttons[ id ].enable(); }, /** * Gets the number of pages in the dialog. * @returns {Number} Page count. */ getPageCount : function() { return this._.pageCount; }, /** * Gets the editor instance which opened this dialog. * @returns {CKEDITOR.editor} Parent editor instances. */ getParentEditor : function() { return this._.editor; }, /** * Gets the element that was selected when opening the dialog, if any. * @returns {CKEDITOR.dom.element} The element that was selected, or null. */ getSelectedElement : function() { return this.getParentEditor().getSelection().getSelectedElement(); }, /** * Adds element to dialog's focusable list. * * @param {CKEDITOR.dom.element} element * @param {Number} [index] */ addFocusable: function( element, index ) { if ( typeof index == 'undefined' ) { index = this._.focusList.length; this._.focusList.push( new Focusable( this, element, index ) ); } else { this._.focusList.splice( index, 0, new Focusable( this, element, index ) ); for ( var i = index + 1 ; i < this._.focusList.length ; i++ ) this._.focusList[ i ].focusIndex++; } } }; CKEDITOR.tools.extend( CKEDITOR.dialog, /** * @lends CKEDITOR.dialog */ { /** * Registers a dialog. * @param {String} name The dialog's name. * @param {Function|String} dialogDefinition * A function returning the dialog's definition, or the URL to the .js file holding the function. * The function should accept an argument "editor" which is the current editor instance, and * return an object conforming to {@link CKEDITOR.dialog.definition}. * @see CKEDITOR.dialog.definition * @example * // Full sample plugin, which does not only register a dialog window but also adds an item to the context menu. * // To open the dialog window, choose "Open dialog" in the context menu. * CKEDITOR.plugins.add( 'myplugin', * { * init: function( editor ) * { * editor.addCommand( 'mydialog',new CKEDITOR.dialogCommand( 'mydialog' ) ); * * if ( editor.contextMenu ) * { * editor.addMenuGroup( 'mygroup', 10 ); * editor.addMenuItem( 'My Dialog', * { * label : 'Open dialog', * command : 'mydialog', * group : 'mygroup' * }); * editor.contextMenu.addListener( function( element ) * { * return { 'My Dialog' : CKEDITOR.TRISTATE_OFF }; * }); * } * * CKEDITOR.dialog.add( 'mydialog', function( api ) * { * // CKEDITOR.dialog.definition * var dialogDefinition = * { * title : 'Sample dialog', * minWidth : 390, * minHeight : 130, * contents : [ * { * id : 'tab1', * label : 'Label', * title : 'Title', * expand : true, * padding : 0, * elements : * [ * { * type : 'html', * html : '<p>This is some sample HTML content.</p>' * }, * { * type : 'textarea', * id : 'textareaId', * rows : 4, * cols : 40 * } * ] * } * ], * buttons : [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ], * onOk : function() { * // "this" is now a CKEDITOR.dialog object. * // Accessing dialog elements: * var textareaObj = this.getContentElement( 'tab1', 'textareaId' ); * alert( "You have entered: " + textareaObj.getValue() ); * } * }; * * return dialogDefinition; * } ); * } * } ); * * CKEDITOR.replace( 'editor1', { extraPlugins : 'myplugin' } ); */ add : function( name, dialogDefinition ) { // Avoid path registration from multiple instances override definition. if ( !this._.dialogDefinitions[name] || typeof dialogDefinition == 'function' ) this._.dialogDefinitions[name] = dialogDefinition; }, exists : function( name ) { return !!this._.dialogDefinitions[ name ]; }, getCurrent : function() { return CKEDITOR.dialog._.currentTop; }, /** * The default OK button for dialogs. Fires the "ok" event and closes the dialog if the event succeeds. * @static * @field * @example * @type Function */ okButton : (function() { var retval = function( editor, override ) { override = override || {}; return CKEDITOR.tools.extend( { id : 'ok', type : 'button', label : editor.lang.common.ok, 'class' : 'cke_dialog_ui_button_ok', onClick : function( evt ) { var dialog = evt.data.dialog; if ( dialog.fire( 'ok', { hide : true } ).hide !== false ) dialog.hide(); } }, override, true ); }; retval.type = 'button'; retval.override = function( override ) { return CKEDITOR.tools.extend( function( editor ){ return retval( editor, override ); }, { type : 'button' }, true ); }; return retval; })(), /** * The default cancel button for dialogs. Fires the "cancel" event and closes the dialog if no UI element value changed. * @static * @field * @example * @type Function */ cancelButton : (function() { var retval = function( editor, override ) { override = override || {}; return CKEDITOR.tools.extend( { id : 'cancel', type : 'button', label : editor.lang.common.cancel, 'class' : 'cke_dialog_ui_button_cancel', onClick : function( evt ) { var dialog = evt.data.dialog; if ( dialog.fire( 'cancel', { hide : true } ).hide !== false ) dialog.hide(); } }, override, true ); }; retval.type = 'button'; retval.override = function( override ) { return CKEDITOR.tools.extend( function( editor ){ return retval( editor, override ); }, { type : 'button' }, true ); }; return retval; })(), /** * Registers a dialog UI element. * @param {String} typeName The name of the UI element. * @param {Function} builder The function to build the UI element. * @example */ addUIElement : function( typeName, builder ) { this._.uiElementBuilders[ typeName ] = builder; } }); CKEDITOR.dialog._ = { uiElementBuilders : {}, dialogDefinitions : {}, currentTop : null, currentZIndex : null }; // "Inherit" (copy actually) from CKEDITOR.event. CKEDITOR.event.implementOn( CKEDITOR.dialog ); CKEDITOR.event.implementOn( CKEDITOR.dialog.prototype, true ); var defaultDialogDefinition = { resizable : CKEDITOR.DIALOG_RESIZE_BOTH, minWidth : 600, minHeight : 400, buttons : [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ] }; // Tool function used to return an item from an array based on its id // property. var getById = function( array, id, recurse ) { for ( var i = 0, item ; ( item = array[ i ] ) ; i++ ) { if ( item.id == id ) return item; if ( recurse && item[ recurse ] ) { var retval = getById( item[ recurse ], id, recurse ) ; if ( retval ) return retval; } } return null; }; // Tool function used to add an item into an array. var addById = function( array, newItem, nextSiblingId, recurse, nullIfNotFound ) { if ( nextSiblingId ) { for ( var i = 0, item ; ( item = array[ i ] ) ; i++ ) { if ( item.id == nextSiblingId ) { array.splice( i, 0, newItem ); return newItem; } if ( recurse && item[ recurse ] ) { var retval = addById( item[ recurse ], newItem, nextSiblingId, recurse, true ); if ( retval ) return retval; } } if ( nullIfNotFound ) return null; } array.push( newItem ); return newItem; }; // Tool function used to remove an item from an array based on its id. var removeById = function( array, id, recurse ) { for ( var i = 0, item ; ( item = array[ i ] ) ; i++ ) { if ( item.id == id ) return array.splice( i, 1 ); if ( recurse && item[ recurse ] ) { var retval = removeById( item[ recurse ], id, recurse ); if ( retval ) return retval; } } return null; }; /** * This class is not really part of the API. It is the "definition" property value * passed to "dialogDefinition" event handlers. * @constructor * @name CKEDITOR.dialog.definitionObject * @extends CKEDITOR.dialog.definition * @example * CKEDITOR.on( 'dialogDefinition', function( evt ) * { * var definition = evt.data.definition; * var content = definition.getContents( 'page1' ); * ... * } ); */ var definitionObject = function( dialog, dialogDefinition ) { // TODO : Check if needed. this.dialog = dialog; // Transform the contents entries in contentObjects. var contents = dialogDefinition.contents; for ( var i = 0, content ; ( content = contents[i] ) ; i++ ) contents[ i ] = content && new contentObject( dialog, content ); CKEDITOR.tools.extend( this, dialogDefinition ); }; definitionObject.prototype = /** @lends CKEDITOR.dialog.definitionObject.prototype */ { /** * Gets a content definition. * @param {String} id The id of the content definition. * @returns {CKEDITOR.dialog.definition.content} The content definition * matching id. */ getContents : function( id ) { return getById( this.contents, id ); }, /** * Gets a button definition. * @param {String} id The id of the button definition. * @returns {CKEDITOR.dialog.definition.button} The button definition * matching id. */ getButton : function( id ) { return getById( this.buttons, id ); }, /** * Adds a content definition object under this dialog definition. * @param {CKEDITOR.dialog.definition.content} contentDefinition The * content definition. * @param {String} [nextSiblingId] The id of an existing content * definition which the new content definition will be inserted * before. Omit if the new content definition is to be inserted as * the last item. * @returns {CKEDITOR.dialog.definition.content} The inserted content * definition. */ addContents : function( contentDefinition, nextSiblingId ) { return addById( this.contents, contentDefinition, nextSiblingId ); }, /** * Adds a button definition object under this dialog definition. * @param {CKEDITOR.dialog.definition.button} buttonDefinition The * button definition. * @param {String} [nextSiblingId] The id of an existing button * definition which the new button definition will be inserted * before. Omit if the new button definition is to be inserted as * the last item. * @returns {CKEDITOR.dialog.definition.button} The inserted button * definition. */ addButton : function( buttonDefinition, nextSiblingId ) { return addById( this.buttons, buttonDefinition, nextSiblingId ); }, /** * Removes a content definition from this dialog definition. * @param {String} id The id of the content definition to be removed. * @returns {CKEDITOR.dialog.definition.content} The removed content * definition. */ removeContents : function( id ) { removeById( this.contents, id ); }, /** * Removes a button definition from the dialog definition. * @param {String} id The id of the button definition to be removed. * @returns {CKEDITOR.dialog.definition.button} The removed button * definition. */ removeButton : function( id ) { removeById( this.buttons, id ); } }; /** * This class is not really part of the API. It is the template of the * objects representing content pages inside the * CKEDITOR.dialog.definitionObject. * @constructor * @name CKEDITOR.dialog.definition.contentObject * @example * CKEDITOR.on( 'dialogDefinition', function( evt ) * { * var definition = evt.data.definition; * var content = definition.getContents( 'page1' ); * content.remove( 'textInput1' ); * ... * } ); */ function contentObject( dialog, contentDefinition ) { this._ = { dialog : dialog }; CKEDITOR.tools.extend( this, contentDefinition ); } contentObject.prototype = /** @lends CKEDITOR.dialog.definition.contentObject.prototype */ { /** * Gets a UI element definition under the content definition. * @param {String} id The id of the UI element definition. * @returns {CKEDITOR.dialog.definition.uiElement} */ get : function( id ) { return getById( this.elements, id, 'children' ); }, /** * Adds a UI element definition to the content definition. * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition The * UI elemnet definition to be added. * @param {String} nextSiblingId The id of an existing UI element * definition which the new UI element definition will be inserted * before. Omit if the new button definition is to be inserted as * the last item. * @returns {CKEDITOR.dialog.definition.uiElement} The element * definition inserted. */ add : function( elementDefinition, nextSiblingId ) { return addById( this.elements, elementDefinition, nextSiblingId, 'children' ); }, /** * Removes a UI element definition from the content definition. * @param {String} id The id of the UI element definition to be * removed. * @returns {CKEDITOR.dialog.definition.uiElement} The element * definition removed. * @example */ remove : function( id ) { removeById( this.elements, id, 'children' ); } }; function initDragAndDrop( dialog ) { var lastCoords = null, abstractDialogCoords = null, element = dialog.getElement().getFirst(), editor = dialog.getParentEditor(), magnetDistance = editor.config.dialog_magnetDistance, margins = editor.skin.margins || [ 0, 0, 0, 0 ]; if ( typeof magnetDistance == 'undefined' ) magnetDistance = 20; function mouseMoveHandler( evt ) { var dialogSize = dialog.getSize(), viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(), x = evt.data.$.screenX, y = evt.data.$.screenY, dx = x - lastCoords.x, dy = y - lastCoords.y, realX, realY; lastCoords = { x : x, y : y }; abstractDialogCoords.x += dx; abstractDialogCoords.y += dy; if ( abstractDialogCoords.x + margins[3] < magnetDistance ) realX = - margins[3]; else if ( abstractDialogCoords.x - margins[1] > viewPaneSize.width - dialogSize.width - magnetDistance ) realX = viewPaneSize.width - dialogSize.width + ( editor.lang.dir == 'rtl' ? 0 : margins[1] ); else realX = abstractDialogCoords.x; if ( abstractDialogCoords.y + margins[0] < magnetDistance ) realY = - margins[0]; else if ( abstractDialogCoords.y - margins[2] > viewPaneSize.height - dialogSize.height - magnetDistance ) realY = viewPaneSize.height - dialogSize.height + margins[2]; else realY = abstractDialogCoords.y; dialog.move( realX, realY, 1 ); evt.data.preventDefault(); } function mouseUpHandler( evt ) { CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler ); CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler ); if ( CKEDITOR.env.ie6Compat ) { var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); coverDoc.removeListener( 'mousemove', mouseMoveHandler ); coverDoc.removeListener( 'mouseup', mouseUpHandler ); } } dialog.parts.title.on( 'mousedown', function( evt ) { lastCoords = { x : evt.data.$.screenX, y : evt.data.$.screenY }; CKEDITOR.document.on( 'mousemove', mouseMoveHandler ); CKEDITOR.document.on( 'mouseup', mouseUpHandler ); abstractDialogCoords = dialog.getPosition(); if ( CKEDITOR.env.ie6Compat ) { var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); coverDoc.on( 'mousemove', mouseMoveHandler ); coverDoc.on( 'mouseup', mouseUpHandler ); } evt.data.preventDefault(); }, dialog ); } function initResizeHandles( dialog ) { var def = dialog.definition, resizable = def.resizable; if ( resizable == CKEDITOR.DIALOG_RESIZE_NONE ) return; var editor = dialog.getParentEditor(); var wrapperWidth, wrapperHeight, viewSize, origin, startSize, dialogCover; var mouseDownFn = CKEDITOR.tools.addFunction( function( $event ) { startSize = dialog.getSize(); var content = dialog.parts.contents, iframeDialog = content.$.getElementsByTagName( 'iframe' ).length; // Shim to help capturing "mousemove" over iframe. if ( iframeDialog ) { dialogCover = CKEDITOR.dom.element.createFromHtml( '
' ); content.append( dialogCover ); } // Calculate the offset between content and chrome size. wrapperHeight = startSize.height - dialog.parts.contents.getSize( 'height', ! ( CKEDITOR.env.gecko || CKEDITOR.env.opera || CKEDITOR.env.ie && CKEDITOR.env.quirks ) ); wrapperWidth = startSize.width - dialog.parts.contents.getSize( 'width', 1 ); origin = { x : $event.screenX, y : $event.screenY }; viewSize = CKEDITOR.document.getWindow().getViewPaneSize(); CKEDITOR.document.on( 'mousemove', mouseMoveHandler ); CKEDITOR.document.on( 'mouseup', mouseUpHandler ); if ( CKEDITOR.env.ie6Compat ) { var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); coverDoc.on( 'mousemove', mouseMoveHandler ); coverDoc.on( 'mouseup', mouseUpHandler ); } $event.preventDefault && $event.preventDefault(); }); // Prepend the grip to the dialog. dialog.on( 'load', function() { var direction = ''; if ( resizable == CKEDITOR.DIALOG_RESIZE_WIDTH ) direction = ' cke_resizer_horizontal'; else if ( resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT ) direction = ' cke_resizer_vertical'; var resizer = CKEDITOR.dom.element.createFromHtml( '' ); dialog.parts.footer.append( resizer, 1 ); }); editor.on( 'destroy', function() { CKEDITOR.tools.removeFunction( mouseDownFn ); } ); function mouseMoveHandler( evt ) { var rtl = editor.lang.dir == 'rtl', dx = ( evt.data.$.screenX - origin.x ) * ( rtl ? -1 : 1 ), dy = evt.data.$.screenY - origin.y, width = startSize.width, height = startSize.height, internalWidth = width + dx * ( dialog._.moved ? 1 : 2 ), internalHeight = height + dy * ( dialog._.moved ? 1 : 2 ), element = dialog._.element.getFirst(), right = rtl && element.getComputedStyle( 'right' ), position = dialog.getPosition(); if ( position.y + internalHeight > viewSize.height ) internalHeight = viewSize.height - position.y; if ( ( rtl ? right : position.x ) + internalWidth > viewSize.width ) internalWidth = viewSize.width - ( rtl ? right : position.x ); // Make sure the dialog will not be resized to the wrong side when it's in the leftmost position for RTL. if ( ( resizable == CKEDITOR.DIALOG_RESIZE_WIDTH || resizable == CKEDITOR.DIALOG_RESIZE_BOTH ) ) width = Math.max( def.minWidth || 0, internalWidth - wrapperWidth ); if ( resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT || resizable == CKEDITOR.DIALOG_RESIZE_BOTH ) height = Math.max( def.minHeight || 0, internalHeight - wrapperHeight ); dialog.resize( width, height ); if ( !dialog._.moved ) dialog.layout(); evt.data.preventDefault(); } function mouseUpHandler() { CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler ); CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler ); if ( dialogCover ) { dialogCover.remove(); dialogCover = null; } if ( CKEDITOR.env.ie6Compat ) { var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); coverDoc.removeListener( 'mouseup', mouseUpHandler ); coverDoc.removeListener( 'mousemove', mouseMoveHandler ); } } } var resizeCover; // Caching resuable covers and allowing only one cover // on screen. var covers = {}, currentCover; function cancelEvent( ev ) { ev.data.preventDefault(1); } function showCover( editor ) { var win = CKEDITOR.document.getWindow(); var config = editor.config, backgroundColorStyle = config.dialog_backgroundCoverColor || 'white', backgroundCoverOpacity = config.dialog_backgroundCoverOpacity, baseFloatZIndex = config.baseFloatZIndex, coverKey = CKEDITOR.tools.genKey( backgroundColorStyle, backgroundCoverOpacity, baseFloatZIndex ), coverElement = covers[ coverKey ]; if ( !coverElement ) { var html = [ '0 ) html.push( 'style="', styles.join( '; ' ), '" ' ); html.push( ' class="cke_dialog_ui_vbox_child">', childHtmlList[i], ' |
Note that this event is called only the first time a specific dialog is * opened. Successive openings will use the cached dialog, and this event will * not get fired.
* @name CKEDITOR#dialogDefinition * @event * @param {CKEDITOR.dialog.definition} data The dialog defination that * is being loaded. * @param {CKEDITOR.editor} editor The editor instance that will use the * dialog. */ /** * Fired when a tab is going to be selected in a dialog * @name CKEDITOR.dialog#selectPage * @event * @param {String} page The id of the page that it's gonna be selected. * @param {String} currentPage The id of the current page. */ /** * Fired when the user tries to dismiss a dialog * @name CKEDITOR.dialog#cancel * @event * @param {Boolean} hide Whether the event should proceed or not. */ /** * Fired when the user tries to confirm a dialog * @name CKEDITOR.dialog#ok * @event * @param {Boolean} hide Whether the event should proceed or not. */ /** * Fired when a dialog is shown * @name CKEDITOR.dialog#show * @event */ /** * Fired when a dialog is shown * @name CKEDITOR.editor#dialogShow * @event */ /** * Fired when a dialog is hidden * @name CKEDITOR.dialog#hide * @event */ /** * Fired when a dialog is hidden * @name CKEDITOR.editor#dialogHide * @event */ /** * Fired when a dialog is being resized. The event is fired on * both the 'CKEDITOR.dialog' object and the dialog instance * since 3.5.3, previously it's available only in the global object. * @name CKEDITOR.dialog#resize * @since 3.5 * @event * @param {CKEDITOR.dialog} dialog The dialog being resized (if * it's fired on the dialog itself, this parameter isn't sent). * @param {String} skin The skin name. * @param {Number} width The new width. * @param {Number} height The new height. */