/*\r
-Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.\r
+Copyright (c) 2003-2011, CKSource - Frederico Knabben. All rights reserved.\r
For licensing, see LICENSE.html or http://ckeditor.com/license\r
*/\r
\r
{\r
this._ || ( this._ = {} );\r
this._['default'] = this._.initValue = elementDefinition['default'] || '';\r
+ this._.required = elementDefinition[ 'required' ] || false;\r
var args = [ this._ ];\r
for ( var i = 1 ; i < arguments.length ; i++ )\r
args.push( arguments[i] );\r
return new CKEDITOR.ui.dialog[elementDefinition.type]( dialog, elementDefinition, output );\r
}\r
},\r
+ containerBuilder =\r
+ {\r
+ build : function( dialog, elementDefinition, output )\r
+ {\r
+ var children = elementDefinition.children,\r
+ child,\r
+ childHtmlList = [],\r
+ childObjList = [];\r
+ for ( var i = 0 ; ( i < children.length && ( child = children[i] ) ) ; i++ )\r
+ {\r
+ var childHtml = [];\r
+ childHtmlList.push( childHtml );\r
+ childObjList.push( CKEDITOR.dialog._.uiElementBuilders[ child.type ].build( dialog, child, childHtml ) );\r
+ }\r
+ return new CKEDITOR.ui.dialog[ elementDefinition.type ]( dialog, childObjList, childHtmlList, output, elementDefinition );\r
+ }\r
+ },\r
commonPrototype =\r
{\r
isChanged : function()\r
return this.getValue() != this.getInitValue();\r
},\r
\r
- reset : function()\r
+ reset : function( noChangeEvent )\r
{\r
- this.setValue( this.getInitValue() );\r
+ this.setValue( this.getInitValue(), noChangeEvent );\r
},\r
\r
setInitValue : function()\r
{\r
dialog.on( 'load', function()\r
{\r
- this.getInputElement().on( 'change', function(){ this.fire( 'change', { value : this.getValue() } ); }, this );\r
+ this.getInputElement().on( 'change', function()\r
+ {\r
+ // Make sure 'onchange' doesn't get fired after dialog closed. (#5719)\r
+ if ( !dialog.parts.dialog.isVisible() )\r
+ return;\r
+\r
+ this.fire( 'change', { value : this.getValue() } );\r
+ }, this );\r
}, this );\r
this._.domOnChangeRegistered = true;\r
}\r
* @extends CKEDITOR.ui.dialog.uiElement\r
* @param {CKEDITOR.dialog} dialog\r
* Parent dialog object.\r
- * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition\r
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition\r
* The element definition. Accepted fields:\r
* <ul>\r
* <li><strong>label</strong> (Required) The label string.</li>\r
return;\r
\r
var _ = initPrivateObject.call( this, elementDefinition );\r
- _.labelId = CKEDITOR.tools.getNextNumber() + '_label';\r
+ _.labelId = CKEDITOR.tools.getNextId() + '_label';\r
var children = this._.children = [];\r
/** @ignore */\r
var innerHTML = function()\r
{\r
- var html = [];\r
+ var html = [],\r
+ requiredClass = elementDefinition.required ? ' cke_required' : '' ;\r
if ( elementDefinition.labelLayout != 'horizontal' )\r
- html.push( '<div class="cke_dialog_ui_labeled_label" id="',\r
- _.labelId,\r
- '" >',\r
+ html.push( '<label class="cke_dialog_ui_labeled_label' + requiredClass + '" ',\r
+ ' id="'+ _.labelId + '"',\r
+ ' for="' + _.inputId + '"',\r
+ ( elementDefinition.labelStyle ? ' style="' + elementDefinition.labelStyle + '"' : '' ) +'>',\r
elementDefinition.label,\r
- '</div>',\r
- '<div class="cke_dialog_ui_labeled_content">',\r
- contentHtml( dialog, elementDefinition ),\r
+ '</label>',\r
+ '<div class="cke_dialog_ui_labeled_content"' + ( elementDefinition.controlStyle ? ' style="' + elementDefinition.controlStyle + '"' : '' ) + ' role="presentation">',\r
+ contentHtml.call( this, dialog, elementDefinition ),\r
'</div>' );\r
else\r
{\r
[\r
{\r
type : 'html',\r
- html : '<span class="cke_dialog_ui_labeled_label" ' +\r
- 'id="' + _.labelId + '">' + CKEDITOR.tools.htmlEncode( elementDefinition.label ) +\r
+ html : '<label class="cke_dialog_ui_labeled_label' + requiredClass + '"' +\r
+ ' id="' + _.labelId + '"' +\r
+ ' for="' + _.inputId + '"' +\r
+ ( elementDefinition.labelStyle ? ' style="' + elementDefinition.labelStyle + '"' : '' ) +'>' +\r
+ CKEDITOR.tools.htmlEncode( elementDefinition.label ) +\r
'</span>'\r
},\r
{\r
type : 'html',\r
- html : '<span class="cke_dialog_ui_labeled_content">' +\r
- contentHtml( dialog, elementDefinition ) +\r
+ html : '<span class="cke_dialog_ui_labeled_content"' + ( elementDefinition.controlStyle ? ' style="' + elementDefinition.controlStyle + '"' : '' ) + '>' +\r
+ contentHtml.call( this, dialog, elementDefinition ) +\r
'</span>'\r
}\r
]\r
}\r
return html.join( '' );\r
};\r
- CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'div', null, null, innerHTML );\r
+ CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'div', null, { role : 'presentation' }, innerHTML );\r
},\r
\r
/**\r
* @extends CKEDITOR.ui.dialog.labeledElement\r
* @param {CKEDITOR.dialog} dialog\r
* Parent dialog object.\r
- * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition\r
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition\r
* The element definition. Accepted fields:\r
* <ul>\r
* <li><strong>default</strong> (Optional) The default value.</li>\r
return;\r
\r
initPrivateObject.call( this, elementDefinition );\r
- var domId = this._.inputId = CKEDITOR.tools.getNextNumber() + '_textInput',\r
+ var domId = this._.inputId = CKEDITOR.tools.getNextId() + '_textInput',\r
attributes = { 'class' : 'cke_dialog_ui_input_' + elementDefinition.type, id : domId, type : 'text' },\r
i;\r
\r
if ( elementDefinition.size )\r
attributes.size = elementDefinition.size;\r
\r
+ if ( elementDefinition.inputStyle )\r
+ attributes.style = elementDefinition.inputStyle;\r
+\r
// If user presses Enter in a text box, it implies clicking OK for the dialog.\r
var me = this, keyPressedOnMe = false;\r
dialog.on( 'load', function()\r
{\r
// IE BUG: Text input fields in IE at 100% would exceed a <td> or inline\r
// container's width, so need to wrap it inside a <div>.\r
- var html = [ '<div class="cke_dialog_ui_input_', elementDefinition.type, '"' ];\r
+ var html = [ '<div class="cke_dialog_ui_input_', elementDefinition.type, '" role="presentation"' ];\r
\r
if ( elementDefinition.width )\r
html.push( 'style="width:'+ elementDefinition.width +'" ' );\r
\r
html.push( '><input ' );\r
+\r
+ attributes[ 'aria-labelledby' ] = this._.labelId;\r
+ this._.required && ( attributes[ 'aria-required' ] = this._.required );\r
for ( var i in attributes )\r
html.push( i + '="' + attributes[i] + '" ' );\r
html.push( ' /></div>' );\r
* @example\r
* @param {CKEDITOR.dialog} dialog\r
* Parent dialog object.\r
- * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition\r
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition\r
* The element definition. Accepted fields:\r
* <ul>\r
* <li><strong>rows</strong> (Optional) The number of rows displayed.\r
\r
initPrivateObject.call( this, elementDefinition );\r
var me = this,\r
- domId = this._.inputId = CKEDITOR.tools.getNextNumber() + '_textarea',\r
+ domId = this._.inputId = CKEDITOR.tools.getNextId() + '_textarea',\r
attributes = {};\r
\r
if ( elementDefinition.validate )\r
attributes.rows = elementDefinition.rows || 5;\r
attributes.cols = elementDefinition.cols || 20;\r
\r
+ if ( typeof elementDefinition.inputStyle != 'undefined' )\r
+ attributes.style = elementDefinition.inputStyle;\r
+\r
+\r
/** @ignore */\r
var innerHTML = function()\r
{\r
- var html = [ '<div class="cke_dialog_ui_input_textarea"><textarea class="cke_dialog_ui_input_textarea" id="', domId, '" ' ];\r
+ attributes[ 'aria-labelledby' ] = this._.labelId;\r
+ this._.required && ( attributes[ 'aria-required' ] = this._.required );\r
+ var html = [ '<div class="cke_dialog_ui_input_textarea" role="presentation"><textarea class="cke_dialog_ui_input_textarea" id="', domId, '" ' ];\r
for ( var i in attributes )\r
html.push( i + '="' + CKEDITOR.tools.htmlEncode( attributes[i] ) + '" ' );\r
html.push( '>', CKEDITOR.tools.htmlEncode( me._['default'] ), '</textarea></div>' );\r
* @example\r
* @param {CKEDITOR.dialog} dialog\r
* Parent dialog object.\r
- * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition\r
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition\r
* The element definition. Accepted fields:\r
* <ul>\r
* <li><strong>checked</strong> (Optional) Whether the checkbox is checked\r
{\r
var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition,\r
{\r
- id : elementDefinition.id ? elementDefinition.id + '_checkbox' : CKEDITOR.tools.getNextNumber() + '_checkbox'\r
+ id : elementDefinition.id ? elementDefinition.id + '_checkbox' : CKEDITOR.tools.getNextId() + '_checkbox'\r
}, true ),\r
- html = [],\r
- attributes = { 'class' : 'cke_dialog_ui_checkbox_input', type : 'checkbox' };\r
+ html = [];\r
+\r
+ var labelId = CKEDITOR.tools.getNextId() + '_label';\r
+ var attributes = { 'class' : 'cke_dialog_ui_checkbox_input', type : 'checkbox', 'aria-labelledby' : labelId };\r
cleanInnerDefinition( myDefinition );\r
if ( elementDefinition[ 'default' ] )\r
attributes.checked = 'checked';\r
+\r
+ if ( typeof myDefinition.inputStyle != 'undefined' )\r
+ myDefinition.style = myDefinition.inputStyle;\r
+\r
_.checkbox = new CKEDITOR.ui.dialog.uiElement( dialog, myDefinition, html, 'input', null, attributes );\r
- html.push( ' <label for="', attributes.id, '">',\r
+ html.push( ' <label id="', labelId, '" for="', attributes.id, '"' + ( elementDefinition.labelStyle ? ' style="' + elementDefinition.labelStyle + '"' : '' ) + '>',\r
CKEDITOR.tools.htmlEncode( elementDefinition.label ),\r
'</label>' );\r
return html.join( '' );\r
* @extends CKEDITOR.ui.dialog.labeledElement\r
* @param {CKEDITOR.dialog} dialog\r
* Parent dialog object.\r
- * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition\r
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition\r
* The element definition. Accepted fields:\r
* <ul>\r
* <li><strong>default</strong> (Required) The default value.</li>\r
var innerHTML = function()\r
{\r
var inputHtmlList = [], html = [],\r
- commonAttributes = { 'class' : 'cke_dialog_ui_radio_item' },\r
- commonName = elementDefinition.id ? elementDefinition.id + '_radio' : CKEDITOR.tools.getNextNumber() + '_radio';\r
+ commonAttributes = { 'class' : 'cke_dialog_ui_radio_item', 'aria-labelledby' : this._.labelId },\r
+ commonName = elementDefinition.id ? elementDefinition.id + '_radio' : CKEDITOR.tools.getNextId() + '_radio';\r
for ( var i = 0 ; i < elementDefinition.items.length ; i++ )\r
{\r
var item = elementDefinition.items[i],\r
title = item[2] !== undefined ? item[2] : item[0],\r
value = item[1] !== undefined ? item[1] : item[0],\r
+ inputId = CKEDITOR.tools.getNextId() + '_radio_input',\r
+ labelId = inputId + '_label',\r
inputDefinition = CKEDITOR.tools.extend( {}, elementDefinition,\r
{\r
- id : CKEDITOR.tools.getNextNumber() + '_radio_input',\r
+ id : inputId,\r
title : null,\r
type : null\r
}, true ),\r
labelDefinition = CKEDITOR.tools.extend( {}, inputDefinition,\r
{\r
- id : null,\r
title : title\r
}, true ),\r
inputAttributes =\r
type : 'radio',\r
'class' : 'cke_dialog_ui_radio_input',\r
name : commonName,\r
- value : value\r
+ value : value,\r
+ 'aria-labelledby' : labelId\r
},\r
inputHtml = [];\r
if ( me._['default'] == value )\r
inputAttributes.checked = 'checked';\r
cleanInnerDefinition( inputDefinition );\r
cleanInnerDefinition( labelDefinition );\r
+\r
+ if ( typeof inputDefinition.inputStyle != 'undefined' )\r
+ inputDefinition.style = inputDefinition.inputStyle;\r
+\r
children.push( new CKEDITOR.ui.dialog.uiElement( dialog, inputDefinition, inputHtml, 'input', null, inputAttributes ) );\r
inputHtml.push( ' ' );\r
- new CKEDITOR.ui.dialog.uiElement( dialog, labelDefinition, inputHtml, 'label', null, { 'for' : inputAttributes.id },\r
+ new CKEDITOR.ui.dialog.uiElement( dialog, labelDefinition, inputHtml, 'label', null, { id : labelId, 'for' : inputAttributes.id },\r
item[0] );\r
inputHtmlList.push( inputHtml.join( '' ) );\r
}\r
- new CKEDITOR.ui.dialog.hbox( dialog, [], inputHtmlList, html );\r
+ new CKEDITOR.ui.dialog.hbox( dialog, children, inputHtmlList, html );\r
return html.join( '' );\r
};\r
\r
* @extends CKEDITOR.ui.dialog.uiElement\r
* @param {CKEDITOR.dialog} dialog\r
* Parent dialog object.\r
- * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition\r
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition\r
* The element definition. Accepted fields:\r
* <ul>\r
* <li><strong>label</strong> (Required) The button label.</li>\r
me.fire( 'click', { dialog : me.getDialog() } );\r
evt.data.preventDefault();\r
} );\r
+\r
+ element.on( 'keydown', function( evt )\r
+ {\r
+ if ( evt.data.getKeystroke() in { 32:1 } )\r
+ {\r
+ me.click();\r
+ evt.data.preventDefault();\r
+ }\r
+ } );\r
})();\r
\r
element.unselectable();\r
var outerDefinition = CKEDITOR.tools.extend( {}, elementDefinition );\r
delete outerDefinition.style;\r
\r
+ var labelId = CKEDITOR.tools.getNextId() + '_label';\r
CKEDITOR.ui.dialog.uiElement.call(\r
this,\r
dialog,\r
href : 'javascript:void(0)',\r
title : elementDefinition.label,\r
hidefocus : 'true',\r
- 'class' : elementDefinition['class']\r
+ 'class' : elementDefinition['class'],\r
+ role : 'button',\r
+ 'aria-labelledby' : labelId\r
},\r
- '<span class="cke_dialog_ui_button">' +\r
+ '<span id="' + labelId + '" class="cke_dialog_ui_button">' +\r
CKEDITOR.tools.htmlEncode( elementDefinition.label ) +\r
'</span>' );\r
},\r
* @constructor\r
* @param {CKEDITOR.dialog} dialog\r
* Parent dialog object.\r
- * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition\r
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition\r
* The element definition. Accepted fields:\r
* <ul>\r
* <li><strong>default</strong> (Required) The default value.</li>\r
if ( elementDefinition.validate )\r
this.validate = elementDefinition.validate;\r
\r
+ _.inputId = CKEDITOR.tools.getNextId() + '_select';\r
/** @ignore */\r
var innerHTML = function()\r
{\r
var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition,\r
{\r
- id : elementDefinition.id ? elementDefinition.id + '_select' : CKEDITOR.tools.getNextNumber() + '_select'\r
+ id : elementDefinition.id ? elementDefinition.id + '_select' : CKEDITOR.tools.getNextId() + '_select'\r
}, true ),\r
html = [],\r
innerHTML = [],\r
- attributes = { 'class' : 'cke_dialog_ui_input_select' };\r
+ attributes = { 'id' : _.inputId, 'class' : 'cke_dialog_ui_input_select', 'aria-labelledby' : this._.labelId };\r
\r
// Add multiple and size attributes from element definition.\r
if ( elementDefinition.size != undefined )\r
for ( var i = 0, item ; i < elementDefinition.items.length && ( item = elementDefinition.items[i] ) ; i++ )\r
{\r
innerHTML.push( '<option value="',\r
- CKEDITOR.tools.htmlEncode( item[1] !== undefined ? item[1] : item[0] ), '" /> ',\r
+ CKEDITOR.tools.htmlEncode( item[1] !== undefined ? item[1] : item[0] ).replace( /"/g, '"' ), '" /> ',\r
CKEDITOR.tools.htmlEncode( item[0] ) );\r
}\r
\r
+ if ( typeof myDefinition.inputStyle != 'undefined' )\r
+ myDefinition.style = myDefinition.inputStyle;\r
+\r
_.select = new CKEDITOR.ui.dialog.uiElement( dialog, myDefinition, html, 'select', null, attributes, innerHTML.join( '' ) );\r
return html.join( '' );\r
};\r
* @constructor\r
* @param {CKEDITOR.dialog} dialog\r
* Parent dialog object.\r
- * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition\r
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition\r
* The element definition. Accepted fields:\r
* <ul>\r
* <li><strong>validate</strong> (Optional) The validation function.</li>\r
/** @ignore */\r
var innerHTML = function()\r
{\r
- _.frameId = CKEDITOR.tools.getNextNumber() + '_fileInput';\r
+ _.frameId = CKEDITOR.tools.getNextId() + '_fileInput';\r
\r
// Support for custom document.domain in IE.\r
var isCustomDomain = CKEDITOR.env.isCustomDomain();\r
* @constructor\r
* @param {CKEDITOR.dialog} dialog\r
* Parent dialog object.\r
- * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition\r
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition\r
* The element definition. Accepted fields:\r
* <ul>\r
* <li><strong>for</strong> (Required) The file input's page and element Id\r
* @extends CKEDITOR.ui.dialog.uiElement\r
* @name CKEDITOR.ui.dialog.html\r
* @param {CKEDITOR.dialog} dialog Parent dialog object.\r
- * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition Element definition.\r
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition Element definition.\r
* Accepted fields:\r
* <ul>\r
* <li><strong>html</strong> (Required) HTML code of this element.</li>\r
theirHtml = '<span>' + theirHtml + '</span>';\r
\r
// Look for focus function in definition.\r
- if ( elementDefinition.focus )\r
+ var focus = elementDefinition.focus;\r
+ if ( focus )\r
{\r
var oldFocus = this.focus;\r
this.focus = function()\r
{\r
oldFocus.call( this );\r
- elementDefinition.focus.call( this );\r
+ typeof focus == 'function' && focus.call( this );\r
this.fire( 'focus' );\r
};\r
if ( elementDefinition.isFocusable )\r
\r
htmlList.push( [ theirMatch[1], ' ', myMatch[1] || '', theirMatch[2] ].join( '' ) );\r
};\r
- })()\r
+ })(),\r
+\r
+ /**\r
+ * Form fieldset for grouping dialog UI elements.\r
+ * @constructor\r
+ * @extends CKEDITOR.ui.dialog.uiElement\r
+ * @param {CKEDITOR.dialog} dialog Parent dialog object.\r
+ * @param {Array} childObjList\r
+ * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this\r
+ * container.\r
+ * @param {Array} childHtmlList\r
+ * Array of HTML code that correspond to the HTML output of all the\r
+ * objects in childObjList.\r
+ * @param {Array} htmlList\r
+ * Array of HTML code that this element will output to.\r
+ * @param {CKEDITOR.dialog.definition.uiElement} elementDefinition\r
+ * The element definition. Accepted fields:\r
+ * <ul>\r
+ * <li><strong>label</strong> (Optional) The legend of the this fieldset.</li>\r
+ * <li><strong>children</strong> (Required) An array of dialog field definitions which will be grouped inside this fieldset. </li>\r
+ * </ul>\r
+ */\r
+ fieldset : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition )\r
+ {\r
+ var legendLabel = elementDefinition.label;\r
+ /** @ignore */\r
+ var innerHTML = function()\r
+ {\r
+ var html = [];\r
+ legendLabel && html.push( '<legend>' + legendLabel + '</legend>' );\r
+ for ( var i = 0; i < childHtmlList.length; i++ )\r
+ html.push( childHtmlList[ i ] );\r
+ return html.join( '' );\r
+ };\r
+\r
+ this._ = { children : childObjList };\r
+ CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'fieldset', null, null, innerHTML );\r
+ }\r
+\r
}, true );\r
\r
CKEDITOR.ui.dialog.html.prototype = new CKEDITOR.ui.dialog.uiElement;\r
{\r
this._.disabled = false;\r
var element = this.getElement();\r
- element && element.removeClass( 'disabled' );\r
+ element && element.removeClass( 'cke_disabled' );\r
},\r
\r
/**\r
disable : function()\r
{\r
this._.disabled = true;\r
- this.getElement().addClass( 'disabled' );\r
+ this.getElement().addClass( 'cke_disabled' );\r
},\r
\r
isVisible : function()\r
setValue : function( value )\r
{\r
!value && ( value = '' );\r
- return CKEDITOR.ui.dialog.uiElement.prototype.setValue.call( this, value );\r
+ return CKEDITOR.ui.dialog.uiElement.prototype.setValue.apply( this, arguments );\r
},\r
\r
keyboardFocusable : true\r
* Sets the state of the checkbox.\r
* @example\r
* @param {Boolean} true to tick the checkbox, false to untick it.\r
+ * @param {Boolean} noChangeEvent Internal commit, to supress 'change' event on this element.\r
*/\r
- setValue : function( checked )\r
+ setValue : function( checked, noChangeEvent )\r
{\r
this.getInputElement().$.checked = checked;\r
- this.fire( 'change', { value : checked } );\r
+ !noChangeEvent && this.fire( 'change', { value : checked } );\r
},\r
\r
/**\r
* Checks one of the radio buttons in this button group.\r
* @example\r
* @param {String} value The value of the button to be chcked.\r
+ * @param {Boolean} noChangeEvent Internal commit, to supress 'change' event on this element.\r
*/\r
- setValue : function( value )\r
+ setValue : function( value, noChangeEvent )\r
{\r
var children = this._.children,\r
item;\r
for ( var i = 0 ; ( i < children.length ) && ( item = children[i] ) ; i++ )\r
item.getElement().$.checked = ( item.getValue() == value );\r
- this.fire( 'change', { value : value } );\r
+ !noChangeEvent && this.fire( 'change', { value : value } );\r
},\r
\r
/**\r
* @returns {String} The value of the action.\r
* @example\r
*/\r
- getAction : function( action )\r
+ getAction : function()\r
{\r
return this.getInputElement().getParent().$.action;\r
},\r
\r
/**\r
+ * The events must be applied on the inner input element, and\r
+ * that must be done when the iframe & form has been loaded\r
+ */\r
+ registerEvents : function( definition )\r
+ {\r
+ var regex = /^on([A-Z]\w+)/,\r
+ match;\r
+\r
+ var registerDomEvent = function( uiElement, dialog, eventName, func )\r
+ {\r
+ uiElement.on( 'formLoaded', function()\r
+ {\r
+ uiElement.getInputElement().on( eventName, func, uiElement );\r
+ });\r
+ };\r
+\r
+ for ( var i in definition )\r
+ {\r
+ if ( !( match = i.match( regex ) ) )\r
+ continue;\r
+\r
+ if ( this.eventProcessors[i] )\r
+ this.eventProcessors[i].call( this, this._.dialog, definition[i] );\r
+ else\r
+ registerDomEvent( this, this._.dialog, match[1].toLowerCase(), definition[i] );\r
+ }\r
+\r
+ return this;\r
+ },\r
+\r
+ /**\r
* Redraws the file input and resets the file path in the file input.\r
* The redraw logic is necessary because non-IE browsers tend to clear\r
* the <iframe> containing the file input after closing the dialog.\r
*/\r
reset : function()\r
{\r
- var frameElement = CKEDITOR.document.getById( this._.frameId ),\r
+ var _ = this._,\r
+ frameElement = CKEDITOR.document.getById( _.frameId ),\r
frameDocument = frameElement.getFrameDocument(),\r
- elementDefinition = this._.definition,\r
- buttons = this._.buttons;\r
+ elementDefinition = _.definition,\r
+ buttons = _.buttons,\r
+ callNumber = this.formLoadedNumber,\r
+ unloadNumber = this.formUnloadNumber,\r
+ langDir = _.dialog._.editor.lang.dir,\r
+ langCode = _.dialog._.editor.langCode;\r
+\r
+ // The callback function for the iframe, but we must call tools.addFunction only once\r
+ // so we store the function number in this.formLoadedNumber\r
+ if ( !callNumber )\r
+ {\r
+ callNumber = this.formLoadedNumber = CKEDITOR.tools.addFunction(\r
+ function()\r
+ {\r
+ // Now we can apply the events to the input type=file\r
+ this.fire( 'formLoaded' ) ;\r
+ }, this ) ;\r
+\r
+ // Remove listeners attached to the content of the iframe (the file input)\r
+ unloadNumber = this.formUnloadNumber = CKEDITOR.tools.addFunction(\r
+ function()\r
+ {\r
+ this.getInputElement().clearCustomData();\r
+ }, this ) ;\r
+\r
+ this.getDialog()._.editor.on( 'destroy', function()\r
+ {\r
+ CKEDITOR.tools.removeFunction( callNumber );\r
+ CKEDITOR.tools.removeFunction( unloadNumber );\r
+ } );\r
+ }\r
\r
function generateFormField()\r
{\r
if ( elementDefinition.size )\r
size = elementDefinition.size - ( CKEDITOR.env.ie ? 7 : 0 ); // "Browse" button is bigger in IE.\r
\r
- frameDocument.$.write( [ '<html><head><title></title></head><body style="margin: 0; overflow: hidden; background: transparent;">',\r
- '<form enctype="multipart/form-data" method="POST" action="',\r
+ frameDocument.$.write( [ '<html dir="' + langDir + '" lang="' + langCode + '"><head><title></title></head><body style="margin: 0; overflow: hidden; background: transparent;">',\r
+ '<form enctype="multipart/form-data" method="POST" dir="' + langDir + '" lang="' + langCode + '" action="',\r
CKEDITOR.tools.htmlEncode( elementDefinition.action ),\r
'">',\r
'<input type="file" name="',\r
CKEDITOR.tools.htmlEncode( size > 0 ? size : "" ),\r
'" />',\r
'</form>',\r
- '</body></html>' ].join( '' ) );\r
+ '</body></html>',\r
+ '<script>window.parent.CKEDITOR.tools.callFunction(' + callNumber + ');',\r
+ 'window.onbeforeunload = function() {window.parent.CKEDITOR.tools.callFunction(' + unloadNumber + ')}</script>' ].join( '' ) );\r
\r
frameDocument.$.close();\r
\r
\r
getValue : function()\r
{\r
- // The file path returned from the input tag is incomplete anyway, so it's\r
- // safe to ignore it and prevent the confirmation dialog from appearing.\r
- // (Part of #3465)\r
- return '';\r
+ return this.getInputElement().$.value || '';\r
+ },\r
+\r
+ /***\r
+ * The default value of input type="file" is an empty string, but during initialization\r
+ * of this UI element, the iframe still isn't ready so it can't be read from that object\r
+ * Setting it manually prevents later issues about the current value ("") being different\r
+ * of the initial value (undefined as it asked for .value of a div)\r
+ */\r
+ setInitValue : function()\r
+ {\r
+ this._.initValue = '';\r
},\r
\r
/**\r
* @type Object\r
* @example\r
*/\r
- eventProcessors : commonEventProcessors,\r
+ eventProcessors :\r
+ {\r
+ onChange : function( dialog, func )\r
+ {\r
+ // If this method is called several times (I'm not sure about how this can happen but the default\r
+ // onChange processor includes this protection)\r
+ // In order to reapply to the new element, the property is deleted at the beggining of the registerEvents method\r
+ if ( !this._.domOnChangeRegistered )\r
+ {\r
+ // By listening for the formLoaded event, this handler will get reapplied when a new\r
+ // form is created\r
+ this.on( 'formLoaded', function()\r
+ {\r
+ this.getInputElement().on( 'change', function(){ this.fire( 'change', { value : this.getValue() } ); }, this );\r
+ }, this );\r
+ this._.domOnChangeRegistered = true;\r
+ }\r
+\r
+ this.on( 'change', func );\r
+ }\r
+ },\r
\r
keyboardFocusable : true\r
}, true );\r
\r
CKEDITOR.ui.dialog.fileButton.prototype = new CKEDITOR.ui.dialog.button;\r
\r
+ CKEDITOR.ui.dialog.fieldset.prototype = CKEDITOR.tools.clone( CKEDITOR.ui.dialog.hbox.prototype );\r
+\r
CKEDITOR.dialog.addUIElement( 'text', textBuilder );\r
CKEDITOR.dialog.addUIElement( 'password', textBuilder );\r
CKEDITOR.dialog.addUIElement( 'textarea', commonBuilder );\r
CKEDITOR.dialog.addUIElement( 'file', commonBuilder );\r
CKEDITOR.dialog.addUIElement( 'fileButton', commonBuilder );\r
CKEDITOR.dialog.addUIElement( 'html', commonBuilder );\r
+ CKEDITOR.dialog.addUIElement( 'fieldset', containerBuilder );\r
})();\r
+\r
+/**\r
+ * Fired when the value of the uiElement is changed\r
+ * @name CKEDITOR.ui.dialog.uiElement#change\r
+ * @event\r
+ */\r
+\r
+/**\r
+ * Fired when the inner frame created by the element is ready.\r
+ * Each time the button is used or the dialog is loaded a new\r
+ * form might be created.\r
+ * @name CKEDITOR.ui.dialog.fileButton#formLoaded\r
+ * @event\r
+ */\r