return null;\r
}\r
\r
+\r
+ function clearOrRecoverTextInputValue( container, isRecover )\r
+ {\r
+ var inputs = container.$.getElementsByTagName( 'input' );\r
+ for ( var i = 0, length = inputs.length; i < length ; i++ )\r
+ {\r
+ var item = new CKEDITOR.dom.element( inputs[ i ] );\r
+\r
+ if ( item.getAttribute( 'type' ).toLowerCase() == 'text' )\r
+ {\r
+ if ( isRecover )\r
+ {\r
+ item.setAttribute( 'value', item.getCustomData( 'fake_value' ) || '' );\r
+ item.removeCustomData( 'fake_value' );\r
+ }\r
+ else\r
+ {\r
+ item.setCustomData( 'fake_value', item.getAttribute( 'value' ) );\r
+ item.setAttribute( 'value', '' );\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
/**\r
* This is the base class for runtime dialog objects. An instance of this\r
* class represents a single named dialog for a single editor instance.\r
{\r
this.on( 'ok', function( evt )\r
{\r
+ // Dialog confirm might probably introduce content changes (#5415).\r
+ editor.fire( 'saveSnapshot' );\r
+ setTimeout( function () { editor.fire( 'saveSnapshot' ); }, 0 );\r
if ( definition.onOk.call( this, evt ) === false )\r
evt.data.hide = false;\r
});\r
{\r
if ( this.fire( 'cancel', { hide : true } ).hide !== false )\r
this.hide();\r
+ evt.data.preventDefault();\r
}, this );\r
\r
// Sort focus list according to tab order definitions.\r
if ( me != CKEDITOR.dialog._.currentTop )\r
return;\r
\r
- var keystroke = evt.data.getKeystroke();\r
+ var keystroke = evt.data.getKeystroke(),\r
+ rtl = editor.lang.dir == 'rtl';\r
\r
processed = 0;\r
if ( keystroke == 9 || keystroke == CKEDITOR.SHIFT + 9 )\r
else if ( ( keystroke == 37 || keystroke == 39 ) && me._.tabBarMode )\r
{\r
// Arrow keys - used for changing tabs.\r
- nextId = ( keystroke == 37 ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ) );\r
+ nextId = ( keystroke == ( rtl ? 39 : 37 ) ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ) );\r
me.selectPage( nextId );\r
me._.tabs[ nextId ][ 0 ].focus();\r
processed = 1;\r
if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) )\r
dialogElement.on( 'keypress', focusKeyPressHandler, this );\r
\r
- if ( CKEDITOR.env.ie6Compat )\r
- {\r
- var coverDoc = coverElement.getChild( 0 ).getFrameDocument();\r
- coverDoc.on( 'keydown', focusKeydownHandler, this, null, 0 );\r
- }\r
} );\r
this.on( 'hide', function()\r
{\r
setupFocus();\r
\r
if ( editor.config.dialog_startupFocusTab\r
- && me._.tabIdList.length > 1 )\r
+ && me._.pageCount > 1 )\r
{\r
me._.tabBarMode = true;\r
me._.tabs[ me._.currentTabId ][ 0 ].focus();\r
\r
// Insert the tabs and contents.\r
for ( var i = 0 ; i < definition.contents.length ; i++ )\r
- this.addPage( definition.contents[i] );\r
+ {\r
+ var page = definition.contents[i];\r
+ page && this.addPage( page );\r
+ }\r
\r
this.parts['tabs'].on( 'click', function( evt )\r
{\r
// If we aren't inside a tab, bail out.\r
if ( target.hasClass( 'cke_dialog_tab' ) )\r
{\r
+ // Get the ID of the tab, without the 'cke_' prefix and the unique number suffix.\r
var id = target.$.id;\r
- this.selectPage( id.substr( 0, id.lastIndexOf( '_' ) ) );\r
+ this.selectPage( id.substring( 4, id.lastIndexOf( '_' ) ) );\r
+\r
if ( this._.tabBarMode )\r
{\r
this._.tabBarMode = false;\r
\r
CKEDITOR.dialog.prototype =\r
{\r
+ destroy : function()\r
+ {\r
+ this.hide();\r
+ this._.element.remove();\r
+ },\r
+\r
/**\r
* Resizes the dialog.\r
* @param {Number} width The width of the dialog in pixels.\r
if ( !( element.getParent() && element.getParent().equals( CKEDITOR.document.getBody() ) ) )\r
element.appendTo( CKEDITOR.document.getBody() );\r
else\r
- return;\r
+ element.setStyle( 'display', 'block' );\r
\r
// FIREFOX BUG: Fix vanishing caret for Firefox 2 or Gecko 1.8.\r
if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 )\r
// First, set the dialog to an appropriate size.\r
this.resize( definition.minWidth, definition.minHeight );\r
\r
- // Select the first tab by default.\r
- this.selectPage( this.definition.contents[0].id );\r
-\r
// Reset all inputs back to their default value.\r
this.reset();\r
\r
+ // Select the first tab by default.\r
+ this.selectPage( this.definition.contents[0].id );\r
+\r
// Set z-index.\r
if ( CKEDITOR.dialog._.currentZIndex === null )\r
CKEDITOR.dialog._.currentZIndex = this._.editor.config.baseFloatZIndex;\r
{\r
CKEDITOR.dialog._.currentTop = this;\r
this._.parentDialog = null;\r
- addCover( this._.editor );\r
+ showCover( this._.editor );\r
\r
element.on( 'keydown', accessKeyDownHandler );\r
element.on( CKEDITOR.env.opera ? 'keypress' : 'keyup', accessKeyUpHandler );\r
for ( var i in this._.contents )\r
{\r
for ( var j in this._.contents[i] )\r
- fn( this._.contents[i][j]);\r
+ fn( this._.contents[i][j] );\r
}\r
return this;\r
},\r
*/\r
reset : (function()\r
{\r
- var fn = function( widget ){ if ( widget.reset ) widget.reset(); };\r
+ var fn = function( widget ){ if ( widget.reset ) widget.reset( 1 ); };\r
return function(){ this.foreach( fn ); return this; };\r
})(),\r
\r
*/\r
hide : function()\r
{\r
+ if ( !this.parts.dialog.isVisible() )\r
+ return;\r
+\r
this.fire( 'hide', {} );\r
this._.editor.fire( 'dialogHide', this );\r
-\r
- // Remove the dialog's element from the root document.\r
var element = this._.element;\r
- if ( !element.getParent() )\r
- return;\r
-\r
- element.remove();\r
+ element.setStyle( 'display', 'none' );\r
this.parts.dialog.setStyle( 'visibility', 'hidden' );\r
-\r
// Unregister all access keys associated with this dialog.\r
unregisterAccessKey( this );\r
\r
+ // Close any child(top) dialogs first.\r
+ while( CKEDITOR.dialog._.currentTop != this )\r
+ CKEDITOR.dialog._.currentTop.hide();\r
+\r
// Maintain dialog ordering and remove cover if needed.\r
if ( !this._.parentDialog )\r
- removeCover();\r
+ hideCover();\r
else\r
{\r
var parentElement = this._.parentDialog.getElement().getFirst();\r
else\r
CKEDITOR.dialog._.currentZIndex -= 10;\r
\r
-\r
+ delete this._.parentDialog;\r
// Reset the initial values of the dialog.\r
this.foreach( function( contentObj ) { contentObj.resetInitValue && contentObj.resetInitValue(); } );\r
},\r
page.setAttribute( 'role', 'tabpanel' );\r
\r
var env = CKEDITOR.env;\r
- var tabId = contents.id + '_' + CKEDITOR.tools.getNextNumber(),\r
+ var tabId = 'cke_' + contents.id + '_' + CKEDITOR.tools.getNextNumber(),\r
tab = CKEDITOR.dom.element.createFromHtml( [\r
'<a class="cke_dialog_tab"',\r
( this._.pageCount > 0 ? ' cke_last' : 'cke_first' ),\r
*/\r
selectPage : function( id )\r
{\r
+ if ( this._.currentTabId == id )\r
+ return;\r
+\r
+ // Returning true means that the event has been canceled\r
+ if ( this.fire( 'selectPage', { page : id, currentPage : this._.currentTabId } ) === true )\r
+ return;\r
+\r
// Hide the non-selected tabs and pages.\r
for ( var i in this._.tabs )\r
{\r
\r
var selected = this._.tabs[id];\r
selected[0].addClass( 'cke_dialog_tab_selected' );\r
- selected[1].show();\r
+\r
+ // [IE] a unvisible input[type='text'] will enlarge it's width\r
+ // if it's value is long when it show( #5649 )\r
+ // so we clear it's value before it shows and then recover it\r
+ if ( CKEDITOR.env.ie6Compat || CKEDITOR.env.ie7Compat )\r
+ {\r
+ clearOrRecoverTextInputValue( selected[1] );\r
+ selected[1].show();\r
+ setTimeout( function()\r
+ {\r
+ clearOrRecoverTextInputValue( selected[1], true );\r
+ }, 0 );\r
+ }\r
+ else\r
+ {\r
+ selected[1].show();\r
+ }\r
+\r
+\r
this._.currentTabId = id;\r
this._.currentTabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, id );\r
},\r
// Transform the contents entries in contentObjects.\r
var contents = dialogDefinition.contents;\r
for ( var i = 0, content ; ( content = contents[i] ) ; i++ )\r
- contents[ i ] = new contentObject( dialog, content );\r
+ contents[ i ] = content && new contentObject( dialog, content );\r
\r
CKEDITOR.tools.extend( this, dialogDefinition );\r
};\r
\r
if ( CKEDITOR.env.ie6Compat )\r
{\r
- var coverDoc = coverElement.getChild( 0 ).getFrameDocument();\r
+ var coverDoc = currentCover.getChild( 0 ).getFrameDocument();\r
coverDoc.removeListener( 'mousemove', mouseMoveHandler );\r
coverDoc.removeListener( 'mouseup', mouseUpHandler );\r
}\r
\r
if ( CKEDITOR.env.ie6Compat )\r
{\r
- var coverDoc = coverElement.getChild( 0 ).getFrameDocument();\r
+ var coverDoc = currentCover.getChild( 0 ).getFrameDocument();\r
coverDoc.on( 'mousemove', mouseMoveHandler );\r
coverDoc.on( 'mouseup', mouseUpHandler );\r
}\r
\r
if ( CKEDITOR.env.ie6Compat )\r
{\r
- var coverDoc = coverElement.getChild( 0 ).getFrameDocument();\r
+ var coverDoc = currentCover.getChild( 0 ).getFrameDocument();\r
coverDoc.on( 'mousemove', mouseMoveHandler, dialog, { part : partName } );\r
coverDoc.on( 'mouseup', mouseUpHandler, dialog, { part : partName } );\r
}\r
\r
if ( CKEDITOR.env.ie6Compat )\r
{\r
- var coverDoc = coverElement.getChild( 0 ).getFrameDocument();\r
+ var coverDoc = currentCover.getChild( 0 ).getFrameDocument();\r
coverDoc.removeListener( 'mouseup', mouseUpHandler );\r
coverDoc.removeListener( 'mousemove', mouseMoveHandler );\r
}\r
}\r
\r
var resizeCover;\r
- var coverElement;\r
+ // Caching resuable covers and allowing only one cover\r
+ // on screen.\r
+ var covers = {},\r
+ currentCover;\r
\r
- var addCover = function( editor )\r
+ function showCover( editor )\r
{\r
var win = CKEDITOR.document.getWindow();\r
+ var backgroundColorStyle = editor.config.dialog_backgroundCoverColor || 'white',\r
+ backgroundCoverOpacity = editor.config.dialog_backgroundCoverOpacity,\r
+ baseFloatZIndex = editor.config.baseFloatZIndex,\r
+ coverKey = CKEDITOR.tools.genKey(\r
+ backgroundColorStyle,\r
+ backgroundCoverOpacity,\r
+ baseFloatZIndex ),\r
+ coverElement = covers[ coverKey ];\r
\r
if ( !coverElement )\r
{\r
- var backgroundColorStyle = editor.config.dialog_backgroundCoverColor || 'white';\r
-\r
var html = [\r
'<div style="position: ', ( CKEDITOR.env.ie6Compat ? 'absolute' : 'fixed' ),\r
- '; z-index: ', editor.config.baseFloatZIndex,\r
+ '; z-index: ', baseFloatZIndex,\r
'; top: 0px; left: 0px; ',\r
( !CKEDITOR.env.ie6Compat ? 'background-color: ' + backgroundColorStyle : '' ),\r
- '" id="cke_dialog_background_cover">'\r
+ '" class="cke_dialog_background_cover">'\r
];\r
\r
-\r
if ( CKEDITOR.env.ie6Compat )\r
{\r
// Support for custom document.domain in IE.\r
html.push( '</div>' );\r
\r
coverElement = CKEDITOR.dom.element.createFromHtml( html.join( '' ) );\r
- }\r
+ coverElement.setOpacity( backgroundCoverOpacity != undefined ? backgroundCoverOpacity : 0.5 );\r
\r
- var element = coverElement;\r
+ coverElement.appendTo( CKEDITOR.document.getBody() );\r
+ covers[ coverKey ] = coverElement;\r
+ }\r
+ else\r
+ coverElement. show();\r
\r
+ currentCover = coverElement;\r
var resizeFunc = function()\r
{\r
var size = win.getViewPaneSize();\r
- element.setStyles(\r
+ coverElement.setStyles(\r
{\r
width : size.width + 'px',\r
height : size.height + 'px'\r
{\r
var pos = win.getScrollPosition(),\r
cursor = CKEDITOR.dialog._.currentTop;\r
- element.setStyles(\r
+ coverElement.setStyles(\r
{\r
left : pos.x + 'px',\r
top : pos.y + 'px'\r
}, 0 );\r
scrollFunc();\r
}\r
+ }\r
\r
- var opacity = editor.config.dialog_backgroundCoverOpacity;\r
- element.setOpacity( typeof opacity != 'undefined' ? opacity : 0.5 );\r
-\r
- element.appendTo( CKEDITOR.document.getBody() );\r
- };\r
-\r
- var removeCover = function()\r
+ function hideCover()\r
{\r
- if ( !coverElement )\r
+ if ( !currentCover )\r
return;\r
\r
var win = CKEDITOR.document.getWindow();\r
- coverElement.remove();\r
+ currentCover.hide();\r
win.removeListener( 'resize', resizeCover );\r
\r
if ( CKEDITOR.env.ie6Compat )\r
}, 0 );\r
}\r
resizeCover = null;\r
- };\r
+ }\r
+\r
+ function removeCovers()\r
+ {\r
+ for ( var coverId in covers )\r
+ covers[ coverId ].remove();\r
+ covers = {};\r
+ }\r
\r
var accessKeyProcessors = {};\r
\r
styles = ( stylesArg && stylesArg.call ? stylesArg( elementDefinition ) : stylesArg ) || {},\r
attributes = ( attributesArg && attributesArg.call ? attributesArg( elementDefinition ) : attributesArg ) || {},\r
innerHTML = ( contentsArg && contentsArg.call ? contentsArg.call( this, dialog, elementDefinition ) : contentsArg ) || '',\r
- domId = this.domId = attributes.id || CKEDITOR.tools.getNextNumber() + '_uiElement',\r
+ domId = this.domId = attributes.id || CKEDITOR.tools.getNextId() + '_uiElement',\r
id = this.id = elementDefinition.id,\r
i;\r
\r
/**\r
* Sets the value of this dialog UI object.\r
* @param {Object} value The new value.\r
+ * @param {Boolean} noChangeEvent Internal commit, to supress 'change' event on this element.\r
* @returns {CKEDITOR.dialog.uiElement} The current UI element.\r
* @example\r
* uiElement.setValue( 'Dingo' );\r
*/\r
- setValue : function( value )\r
+ setValue : function( value, noChangeEvent )\r
{\r
this.getInputElement().setValue( value );\r
- this.fire( 'change', { value : value } );\r
+ !noChangeEvent && this.fire( 'change', { value : value } );\r
return this;\r
},\r
\r
// undo support should dedicate to specific dialog implementation.\r
canUndo: false,\r
\r
- editorFocus : CKEDITOR.env.ie\r
+ editorFocus : CKEDITOR.env.ie || CKEDITOR.env.webkit\r
};\r
\r
(function()\r
return this.functions( function( val ){ return val != value; }, msg );\r
}\r
};\r
+\r
+ CKEDITOR.on( 'instanceDestroyed', function( evt )\r
+ {\r
+ // Remove dialog cover on last instance destroy.\r
+ if ( CKEDITOR.tools.isEmpty( CKEDITOR.instances ) )\r
+ {\r
+ var currentTopDialog;\r
+ while ( ( currentTopDialog = CKEDITOR.dialog._.currentTop ) )\r
+ currentTopDialog.hide();\r
+ removeCovers();\r
+ }\r
+\r
+ var dialogs = evt.editor._.storedDialogs;\r
+ for ( var name in dialogs )\r
+ dialogs[ name ].destroy();\r
+\r
+ });\r
+\r
})();\r
})();\r
\r
* @param {CKEDITOR.editor} editor The editor instance that will use the\r
* dialog.\r
*/\r
+\r
+/**\r
+ * Fired when a tab is going to be selected in a dialog\r
+ * @name dialog#selectPage\r
+ * @event\r
+ * @param String page The id of the page that it's gonna be selected.\r
+ * @param String currentPage The id of the current page.\r
+ */\r