X-Git-Url: https://jasonwoof.com/gitweb/?a=blobdiff_plain;f=_source%2Fplugins%2Fdialog%2Fplugin.js;h=9927ebee360fb8d32cd6420a59c29808daa677d0;hb=e371ddf8abcb89013e20e6d0dd746adec344d0e5;hp=75067d6ad28cbf91765a50553f9cbcaa6ff00a63;hpb=7cd80714081a8ffdf4a1a8d2c72f120ed5ef3d6d;p=ckeditor.git
diff --git a/_source/plugins/dialog/plugin.js b/_source/plugins/dialog/plugin.js
index 75067d6..9927ebe 100644
--- a/_source/plugins/dialog/plugin.js
+++ b/_source/plugins/dialog/plugin.js
@@ -1,5 +1,5 @@
/*
-Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved.
+Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
For licensing, see LICENSE.html or http://ckeditor.com/license
*/
@@ -7,11 +7,6 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
* @fileOverview The floating dialog plugin.
*/
-CKEDITOR.plugins.add( 'dialog',
- {
- requires : [ 'dialogui' ]
- });
-
/**
* No resize for this dialog.
* @constant
@@ -73,9 +68,6 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
return null;
}
- // Stores dialog related data from skin definitions. e.g. margin sizes.
- var skinData = {};
-
/**
* This is the base class for runtime dialog objects. An instance of this
* class represents a single named dialog for a single editor instance.
@@ -100,14 +92,6 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
// functions.
definition = new definitionObject( this, definition );
- // Fire the "dialogDefinition" event, making it possible to customize
- // the dialog definition.
- this.definition = definition = CKEDITOR.fire( 'dialogDefinition',
- {
- name : dialogName,
- definition : definition
- }
- , editor ).definition;
var doc = CKEDITOR.document;
@@ -143,6 +127,12 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
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.
this.parts.dialog.setStyles(
@@ -156,6 +146,14 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
// 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;
// Initialize load, show, hide, ok and cancel events.
if ( definition.onLoad )
this.on( 'load', definition.onLoad );
@@ -170,6 +168,9 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
{
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;
});
@@ -250,8 +251,29 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
{
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( forward )
{
var focusList = me._.focusList,
@@ -259,7 +281,17 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
if ( focusList.length < 1 )
return;
- var startIndex = ( me._.currentFocusIndex + offset + focusList.length ) % focusList.length,
+ 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 ( !focusList[ currentIndex ].isFocusable() )
{
@@ -274,6 +306,8 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
focusList[ currentIndex ].select();
}
+ this.changeFocus = changeFocus;
+
var processed;
function focusKeydownHandler( evt )
@@ -282,7 +316,8 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
if ( me != CKEDITOR.dialog._.currentTop )
return;
- var keystroke = evt.data.getKeystroke();
+ var keystroke = evt.data.getKeystroke(),
+ rtl = editor.lang.dir == 'rtl';
processed = 0;
if ( keystroke == 9 || keystroke == CKEDITOR.SHIFT + 9 )
@@ -305,7 +340,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
processed = 1;
}
- else if ( keystroke == CKEDITOR.ALT + 121 && !me._.tabBarMode )
+ 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;
@@ -315,11 +350,19 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
else if ( ( keystroke == 37 || keystroke == 39 ) && me._.tabBarMode )
{
// Arrow keys - used for changing tabs.
- nextId = ( keystroke == 37 ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ) );
+ 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( true );
+ processed = 1;
+ }
if ( processed )
{
@@ -333,26 +376,22 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
processed && evt.data.preventDefault();
}
+ var dialogElement = this._.element;
// Add the dialog keyboard handlers.
this.on( 'show', function()
{
- CKEDITOR.document.on( 'keydown', focusKeydownHandler, this, null, 0 );
+ dialogElement.on( 'keydown', focusKeydownHandler, this, null, 0 );
// 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)
if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) )
- CKEDITOR.document.on( 'keypress', focusKeyPressHandler, this );
+ dialogElement.on( 'keypress', focusKeyPressHandler, this );
- if ( CKEDITOR.env.ie6Compat )
- {
- var coverDoc = coverElement.getChild( 0 ).getFrameDocument();
- coverDoc.on( 'keydown', focusKeydownHandler, this, null, 0 );
- }
} );
this.on( 'hide', function()
{
- CKEDITOR.document.removeListener( 'keydown', focusKeydownHandler );
+ dialogElement.removeListener( 'keydown', focusKeydownHandler );
if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) )
- CKEDITOR.document.removeListener( 'keypress', focusKeyPressHandler );
+ dialogElement.removeListener( 'keypress', focusKeyPressHandler );
} );
this.on( 'iframeAdded', function( evt )
{
@@ -363,10 +402,30 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
// Auto-focus logic in dialog.
this.on( 'show', function()
{
- if ( !this._.hasFocus )
+ // 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;
- changeFocus( true );
+
+ // 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( true );
/*
* IE BUG: If the initial focus went into a non-text element (e.g. button),
@@ -413,30 +472,29 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
// Insert the tabs and contents.
for ( var i = 0 ; i < definition.contents.length ; i++ )
- this.addPage( definition.contents[i] );
+ {
+ var page = definition.contents[i];
+ page && this.addPage( page );
+ }
- var tabRegex = /cke_dialog_tab(\s|$|_)/,
- tabOuterRegex = /cke_dialog_tab(\s|$)/;
this.parts['tabs'].on( 'click', function( evt )
{
- var target = evt.data.getTarget(), firstNode = target, id, page;
-
+ var target = evt.data.getTarget();
// If we aren't inside a tab, bail out.
- if ( !( tabRegex.test( target.$.className ) || target.getName() == 'a' ) )
- return;
-
- // Find the outer
container of the tab.
- id = target.$.id.substr( 0, target.$.id.lastIndexOf( '_' ) );
- this.selectPage( id );
-
- if ( this._.tabBarMode )
+ if ( target.hasClass( 'cke_dialog_tab' ) )
{
- this._.tabBarMode = false;
- this._.currentFocusIndex = -1;
- changeFocus( true );
- }
+ // 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( '_' ) ) );
- evt.data.preventDefault();
+ if ( this._.tabBarMode )
+ {
+ this._.tabBarMode = false;
+ this._.currentFocusIndex = -1;
+ changeFocus( true );
+ }
+ evt.data.preventDefault();
+ }
}, this );
// Insert buttons.
@@ -452,8 +510,6 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
for ( i = 0 ; i < buttons.length ; i++ )
this._.buttons[ buttons[i].id ] = buttons[i];
-
- CKEDITOR.skins.load( editor, 'dialog' );
};
// Focusable interface. Use it via dialog.addFocusable.
@@ -461,6 +517,8 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
{
this.element = element;
this.focusIndex = index;
+ // TODO: support tabIndex for focusables.
+ this.tabIndex = 0;
this.isFocusable = function()
{
return !element.getAttribute( 'disabled' ) && element.isVisible();
@@ -488,6 +546,12 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
CKEDITOR.dialog.prototype =
{
+ destroy : function()
+ {
+ this.hide();
+ this._.element.remove();
+ },
+
/**
* Resizes the dialog.
* @param {Number} width The width of the dialog in pixels.
@@ -604,7 +668,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
if ( !( element.getParent() && element.getParent().equals( CKEDITOR.document.getBody() ) ) )
element.appendTo( CKEDITOR.document.getBody() );
else
- return;
+ element.setStyle( 'display', 'block' );
// FIREFOX BUG: Fix vanishing caret for Firefox 2 or Gecko 1.8.
if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 )
@@ -638,7 +702,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
{
CKEDITOR.dialog._.currentTop = this;
this._.parentDialog = null;
- addCover( this._.editor );
+ showCover( this._.editor );
element.on( 'keydown', accessKeyDownHandler );
element.on( CKEDITOR.env.opera ? 'keypress' : 'keyup', accessKeyUpHandler );
@@ -679,6 +743,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
// Execute onLoad for the first show.
this.fireOnce( 'load', {} );
this.fire( 'show', {} );
+ this._.editor.fire( 'dialogShow', this );
// Save the initial values of the dialog.
this.foreach( function( contentObj ) { contentObj.setInitValue && contentObj.setInitValue(); } );
@@ -741,22 +806,24 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
*/
hide : function()
{
- this.fire( 'hide', {} );
-
- // Remove the dialog's element from the root document.
- var element = this._.element;
- if ( !element.getParent() )
+ if ( !this.parts.dialog.isVisible() )
return;
- element.remove();
+ this.fire( 'hide', {} );
+ this._.editor.fire( 'dialogHide', this );
+ 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 )
- removeCover();
+ hideCover();
else
{
var parentElement = this._.parentDialog.getElement().getFirst();
@@ -789,7 +856,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
else
CKEDITOR.dialog._.currentZIndex -= 10;
-
+ delete this._.parentDialog;
// Reset the initial values of the dialog.
this.foreach( function( contentObj ) { contentObj.resetInitValue && contentObj.resetInitValue(); } );
},
@@ -811,34 +878,37 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
children : contents.elements,
expand : !!contents.expand,
padding : contents.padding,
- style : contents.style || 'width: 100%;'
+ 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( '' ) );
- var tab = CKEDITOR.dom.element.createFromHtml( [
+ 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="', contents.id + '_', CKEDITOR.tools.getNextNumber(), '"' +
- ' href="javascript:void(0)"',
- ' hidefocus="true">',
+ ' id="', tabId, '"',
+ env.gecko && env.version >= 10900 && !env.hc ? '' : ' href="javascript:void(0)"',
+ ' tabIndex="-1"',
+ ' hidefocus="true"',
+ ' role="tab">',
contents.label,
''
].join( '' ) );
- // If only a single page exist, a different style is used in the central pane.
- if ( this._.pageCount === 0 )
- this.parts.dialog.addClass( 'cke_single_page' );
- else
- this.parts.dialog.removeClass( 'cke_single_page' );
+ page.setAttribute( 'aria-labelledby', tabId );
// Take records for the tabs and elements created.
this._.tabs[ contents.id ] = [ tab, page ];
this._.tabIdList.push( contents.id );
- this._.pageCount++;
+ !contents.hidden && this._.pageCount++;
this._.lastTab = tab;
+ this.updateStyle();
var contentMap = this._.contents[ contents.id ] = {},
cursor,
@@ -876,6 +946,13 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
*/
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 )
{
@@ -886,6 +963,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
tab.removeClass( 'cke_dialog_tab_selected' );
page.hide();
}
+ page.setAttribute( 'aria-hidden', i != id );
}
var selected = this._.tabs[id];
@@ -895,6 +973,13 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
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.
@@ -904,9 +989,15 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
hidePage : function( id )
{
var tab = this._.tabs[id] && this._.tabs[id][0];
- if ( !tab )
+ if ( !tab || this._.pageCount == 1 )
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();
},
/**
@@ -921,6 +1012,8 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
if ( !tab )
return;
tab.show();
+ this._.pageCount++;
+ this.updateStyle();
},
/**
@@ -955,7 +1048,8 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
*/
getContentElement : function( pageId, elementId )
{
- return this._.contents[pageId][elementId];
+ var page = this._.contents[ pageId ];
+ return page && page[ elementId ];
},
/**
@@ -1202,12 +1296,15 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
var defaultDialogDefinition =
{
- resizable : CKEDITOR.DIALOG_RESIZE_NONE,
+ resizable : CKEDITOR.DIALOG_RESIZE_BOTH,
minWidth : 600,
minHeight : 400,
buttons : [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ]
};
+ // The buttons in MacOS Apps are in reverse order #4750
+ CKEDITOR.env.mac && defaultDialogDefinition.buttons.reverse();
+
// Tool function used to return an item from an array based on its id
// property.
var getById = function( array, id, recurse )
@@ -1294,7 +1391,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
// Transform the contents entries in contentObjects.
var contents = dialogDefinition.contents;
for ( var i = 0, content ; ( content = contents[i] ) ; i++ )
- contents[ i ] = new contentObject( dialog, content );
+ contents[ i ] = content && new contentObject( dialog, content );
CKEDITOR.tools.extend( this, dialogDefinition );
};
@@ -1454,7 +1551,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
element = dialog.getElement().getFirst(),
editor = dialog.getParentEditor(),
magnetDistance = editor.config.dialog_magnetDistance,
- margins = skinData[ editor.skinName ].margins || [ 0, 0, 0, 0 ];
+ margins = editor.skin.margins || [ 0, 0, 0, 0 ];
if ( typeof magnetDistance == 'undefined' )
magnetDistance = 20;
@@ -1499,7 +1596,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
if ( CKEDITOR.env.ie6Compat )
{
- var coverDoc = coverElement.getChild( 0 ).getFrameDocument();
+ var coverDoc = currentCover.getChild( 0 ).getFrameDocument();
coverDoc.removeListener( 'mousemove', mouseMoveHandler );
coverDoc.removeListener( 'mouseup', mouseUpHandler );
}
@@ -1517,7 +1614,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
if ( CKEDITOR.env.ie6Compat )
{
- var coverDoc = coverElement.getChild( 0 ).getFrameDocument();
+ var coverDoc = currentCover.getChild( 0 ).getFrameDocument();
coverDoc.on( 'mousemove', mouseMoveHandler );
coverDoc.on( 'mouseup', mouseUpHandler );
}
@@ -1532,7 +1629,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
minWidth = definition.minWidth || 0,
minHeight = definition.minHeight || 0,
resizable = definition.resizable,
- margins = skinData[ dialog.getParentEditor().skinName ].margins || [ 0, 0, 0, 0 ];
+ margins = dialog.getParentEditor().skin.margins || [ 0, 0, 0, 0 ];
function topSizer( coords, dy )
{
@@ -1575,7 +1672,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
if ( CKEDITOR.env.ie6Compat )
{
- var coverDoc = coverElement.getChild( 0 ).getFrameDocument();
+ var coverDoc = currentCover.getChild( 0 ).getFrameDocument();
coverDoc.on( 'mousemove', mouseMoveHandler, dialog, { part : partName } );
coverDoc.on( 'mouseup', mouseUpHandler, dialog, { part : partName } );
}
@@ -1646,7 +1743,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
if ( CKEDITOR.env.ie6Compat )
{
- var coverDoc = coverElement.getChild( 0 ).getFrameDocument();
+ var coverDoc = currentCover.getChild( 0 ).getFrameDocument();
coverDoc.removeListener( 'mouseup', mouseUpHandler );
coverDoc.removeListener( 'mousemove', mouseMoveHandler );
}
@@ -1670,25 +1767,33 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
}
var resizeCover;
- var coverElement;
+ // Caching resuable covers and allowing only one cover
+ // on screen.
+ var covers = {},
+ currentCover;
- var addCover = function( editor )
+ function showCover( editor )
{
var win = CKEDITOR.document.getWindow();
+ var backgroundColorStyle = editor.config.dialog_backgroundCoverColor || 'white',
+ backgroundCoverOpacity = editor.config.dialog_backgroundCoverOpacity,
+ baseFloatZIndex = editor.config.baseFloatZIndex,
+ coverKey = CKEDITOR.tools.genKey(
+ backgroundColorStyle,
+ backgroundCoverOpacity,
+ baseFloatZIndex ),
+ coverElement = covers[ coverKey ];
if ( !coverElement )
{
- var backgroundColorStyle = editor.config.dialog_backgroundCoverColor || 'white';
-
var html = [
' '
+ '" class="cke_dialog_background_cover">'
];
-
if ( CKEDITOR.env.ie6Compat )
{
// Support for custom document.domain in IE.
@@ -1724,14 +1829,19 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
html.push( ' ' );
coverElement = CKEDITOR.dom.element.createFromHtml( html.join( '' ) );
- }
+ coverElement.setOpacity( backgroundCoverOpacity != undefined ? backgroundCoverOpacity : 0.5 );
- var element = coverElement;
+ coverElement.appendTo( CKEDITOR.document.getBody() );
+ covers[ coverKey ] = coverElement;
+ }
+ else
+ coverElement. show();
+ currentCover = coverElement;
var resizeFunc = function()
{
var size = win.getViewPaneSize();
- element.setStyles(
+ coverElement.setStyles(
{
width : size.width + 'px',
height : size.height + 'px'
@@ -1742,7 +1852,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
{
var pos = win.getScrollPosition(),
cursor = CKEDITOR.dialog._.currentTop;
- element.setStyles(
+ coverElement.setStyles(
{
left : pos.x + 'px',
top : pos.y + 'px'
@@ -1752,7 +1862,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
{
var dialogPos = cursor.getPosition();
cursor.move( dialogPos.x, dialogPos.y );
- } while( ( cursor = cursor._.parentDialog ) );
+ } while ( ( cursor = cursor._.parentDialog ) );
};
resizeCover = resizeFunc;
@@ -1774,20 +1884,15 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
}, 0 );
scrollFunc();
}
+ }
- var opacity = editor.config.dialog_backgroundCoverOpacity;
- element.setOpacity( typeof opacity != 'undefined' ? opacity : 0.5 );
-
- element.appendTo( CKEDITOR.document.getBody() );
- };
-
- var removeCover = function()
+ function hideCover()
{
- if ( !coverElement )
+ if ( !currentCover )
return;
var win = CKEDITOR.document.getWindow();
- coverElement.remove();
+ currentCover.hide();
win.removeListener( 'resize', resizeCover );
if ( CKEDITOR.env.ie6Compat )
@@ -1799,7 +1904,14 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
}, 0 );
}
resizeCover = null;
- };
+ }
+
+ function removeCovers()
+ {
+ for ( var coverId in covers )
+ covers[ coverId ].remove();
+ covers = {};
+ }
var accessKeyProcessors = {};
@@ -1943,8 +2055,8 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
html = [ '<', nodeName, ' ' ],
styles = ( stylesArg && stylesArg.call ? stylesArg( elementDefinition ) : stylesArg ) || {},
attributes = ( attributesArg && attributesArg.call ? attributesArg( elementDefinition ) : attributesArg ) || {},
- innerHTML = ( contentsArg && contentsArg.call ? contentsArg( dialog, elementDefinition ) : contentsArg ) || '',
- domId = this.domId = attributes.id || CKEDITOR.tools.getNextNumber() + '_uiElement',
+ innerHTML = ( contentsArg && contentsArg.call ? contentsArg.call( this, dialog, elementDefinition ) : contentsArg ) || '',
+ domId = this.domId = attributes.id || CKEDITOR.tools.getNextId() + '_uiElement',
id = this.id = elementDefinition.id,
i;
@@ -2028,6 +2140,8 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
// Register the object as a tab focus if it can be included.
if ( this.keyboardFocusable )
{
+ this.tabIndex = elementDefinition.tabIndex || 0;
+
this.focusIndex = dialog._.focusList.push( this ) - 1;
this.on( 'focus', function()
{
@@ -2090,7 +2204,7 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
className = 'cke_dialog_ui_hbox_first';
if ( i == childHtmlList.length - 1 )
className = 'cke_dialog_ui_hbox_last';
- html.push( ' | | | ' );
return html.join( '' );
};
- CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition || { type : 'vbox' }, htmlList, 'div', null, null, innerHTML );
+ CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition || { type : 'vbox' }, htmlList, 'div', null, { role : 'presentation' }, innerHTML );
}
};
})();
@@ -2569,9 +2686,12 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
{
editor.openDialog( this.dialogName );
},
+
// Dialog commands just open a dialog ui, thus require no undo logic,
// undo support should dedicate to specific dialog implementation.
- canUndo: false
+ canUndo: false,
+
+ editorFocus : CKEDITOR.env.ie || CKEDITOR.env.webkit
};
(function()
@@ -2691,18 +2811,25 @@ CKEDITOR.DIALOG_RESIZE_BOTH = 3;
return this.functions( function( val ){ return val != value; }, msg );
}
};
- })();
- // Grab the margin data from skin definition and store it away.
- CKEDITOR.skins.add = ( function()
+ CKEDITOR.on( 'instanceDestroyed', function( evt )
{
- var original = CKEDITOR.skins.add;
- return function( skinName, skinDefinition )
+ // Remove dialog cover on last instance destroy.
+ if ( CKEDITOR.tools.isEmpty( CKEDITOR.instances ) )
{
- skinData[ skinName ] = { margins : skinDefinition.margins };
- return original.apply( this, arguments );
- };
- } )();
+ var currentTopDialog;
+ while ( ( currentTopDialog = CKEDITOR.dialog._.currentTop ) )
+ currentTopDialog.hide();
+ removeCovers();
+ }
+
+ var dialogs = evt.editor._.storedDialogs;
+ for ( var name in dialogs )
+ dialogs[ name ].destroy();
+
+ });
+
+ })();
})();
// Extend the CKEDITOR.editor class with dialog specific functions.
@@ -2712,17 +2839,19 @@ CKEDITOR.tools.extend( CKEDITOR.editor.prototype,
/**
* Loads and opens a registered dialog.
* @param {String} dialogName The registered name of the dialog.
+ * @param {Function} callback The function to be invoked after dialog instance created.
* @see CKEDITOR.dialog.add
* @example
* CKEDITOR.instances.editor1.openDialog( 'smiley' );
* @returns {CKEDITOR.dialog} The dialog object corresponding to the dialog displayed. null if the dialog name is not registered.
*/
- openDialog : function( dialogName )
+ openDialog : function( dialogName, callback )
{
- var dialogDefinitions = CKEDITOR.dialog._.dialogDefinitions[ dialogName ];
+ var dialogDefinitions = CKEDITOR.dialog._.dialogDefinitions[ dialogName ],
+ dialogSkin = this.skin.dialog;
// If the dialogDefinition is already loaded, open it immediately.
- if ( typeof dialogDefinitions == 'function' )
+ if ( typeof dialogDefinitions == 'function' && dialogSkin._isLoaded )
{
var storedDialogs = this._.storedDialogs ||
( this._.storedDialogs = {} );
@@ -2730,6 +2859,7 @@ CKEDITOR.tools.extend( CKEDITOR.editor.prototype,
var dialog = storedDialogs[ dialogName ] ||
( storedDialogs[ dialogName ] = new CKEDITOR.dialog( this, dialogName ) );
+ callback && callback.call( dialog, dialog );
dialog.show();
return dialog;
@@ -2743,19 +2873,41 @@ CKEDITOR.tools.extend( CKEDITOR.editor.prototype,
me = this;
body.setStyle( 'cursor', 'wait' );
- CKEDITOR.scriptLoader.load( CKEDITOR.getUrl( dialogDefinitions ), function()
- {
- // In case of plugin error, mark it as loading failed.
- if ( typeof CKEDITOR.dialog._.dialogDefinitions[ dialogName ] != 'function' )
- CKEDITOR.dialog._.dialogDefinitions[ dialogName ] = 'failed';
- me.openDialog( dialogName );
- body.setStyle( 'cursor', cursor );
- } );
+
+ function onDialogFileLoaded( success )
+ {
+ var dialogDefinition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ],
+ skin = me.skin.dialog;
+
+ // Check if both skin part and definition is loaded.
+ if ( !skin._isLoaded || loadDefinition && typeof success == 'undefined' )
+ return;
+
+ // In case of plugin error, mark it as loading failed.
+ if ( typeof dialogDefinition != 'function' )
+ CKEDITOR.dialog._.dialogDefinitions[ dialogName ] = 'failed';
+
+ me.openDialog( dialogName, callback );
+ body.setStyle( 'cursor', cursor );
+ }
+
+ if ( typeof dialogDefinitions == 'string' )
+ {
+ var loadDefinition = 1;
+ CKEDITOR.scriptLoader.load( CKEDITOR.getUrl( dialogDefinitions ), onDialogFileLoaded );
+ }
+
+ CKEDITOR.skins.load( this, 'dialog', onDialogFileLoaded );
return null;
}
});
+CKEDITOR.plugins.add( 'dialog',
+ {
+ requires : [ 'dialogui' ]
+ });
+
// Dialog related configurations.
/**
@@ -2779,6 +2931,15 @@ CKEDITOR.tools.extend( CKEDITOR.editor.prototype,
*/
/**
+ * If the dialog has more than one tab, put focus into the first tab as soon as dialog is opened.
+ * @name CKEDITOR.config.dialog_startupFocusTab
+ * @type Boolean
+ * @default false
+ * @example
+ * config.dialog_startupFocusTab = true;
+ */
+
+/**
* The distance of magnetic borders used in moving and resizing dialogs,
* measured in pixels.
* @name CKEDITOR.config.dialog_magnetDistance
@@ -2787,3 +2948,26 @@ CKEDITOR.tools.extend( CKEDITOR.editor.prototype,
* @example
* config.dialog_magnetDistance = 30;
*/
+
+/**
+ * Fired when a dialog definition is about to be used to create a dialog into
+ * an editor instance. This event makes it possible to customize the definition
+ * before creating it.
+ * 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.dialogDefinition} 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 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.
+ */
|