{\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
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( '<label class="cke_dialog_ui_labeled_label" ',\r
+ html.push( '<label class="cke_dialog_ui_labeled_label' + requiredClass + '" ',\r
' id="'+ _.labelId + '"',\r
' for="' + _.inputId + '"',\r
' style="' + elementDefinition.labelStyle + '">',\r
[\r
{\r
type : 'html',\r
- html : '<label class="cke_dialog_ui_labeled_label"' +\r
+ html : '<label class="cke_dialog_ui_labeled_label' + requiredClass + '"' +\r
' id="' + _.labelId + '"' +\r
' for="' + _.inputId + '"' +\r
' style="' + elementDefinition.labelStyle + '">' +\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.controlStyle )\r
+ attributes.style = elementDefinition.controlStyle;\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
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
{\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
\r
- var labelId = CKEDITOR.tools.getNextNumber() + '_label';\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.controlStyle != 'undefined')\r
+ myDefinition.style = myDefinition.controlStyle;\r
+\r
_.checkbox = new CKEDITOR.ui.dialog.uiElement( dialog, myDefinition, html, 'input', null, attributes );\r
html.push( ' <label id="', labelId, '" for="', attributes.id, '">',\r
CKEDITOR.tools.htmlEncode( elementDefinition.label ),\r
{\r
var inputHtmlList = [], html = [],\r
commonAttributes = { 'class' : 'cke_dialog_ui_radio_item', 'aria-labelledby' : this._.labelId },\r
- commonName = elementDefinition.id ? elementDefinition.id + '_radio' : CKEDITOR.tools.getNextNumber() + '_radio';\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.getNextNumber() + '_radio_input',\r
+ inputId = CKEDITOR.tools.getNextId() + '_radio_input',\r
labelId = inputId + '_label',\r
inputDefinition = CKEDITOR.tools.extend( {}, elementDefinition,\r
{\r
inputAttributes.checked = 'checked';\r
cleanInnerDefinition( inputDefinition );\r
cleanInnerDefinition( labelDefinition );\r
+\r
+ if (typeof inputDefinition.controlStyle != 'undefined')\r
+ inputDefinition.style = inputDefinition.controlStyle;\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, { id : labelId, 'for' : inputAttributes.id },\r
\r
element.on( 'keydown', function( evt )\r
{\r
- if ( evt.data.getKeystroke() in { 32:1, 13:1 } )\r
+ if ( evt.data.getKeystroke() in { 32:1 } )\r
{\r
me.click();\r
evt.data.preventDefault();\r
var outerDefinition = CKEDITOR.tools.extend( {}, elementDefinition );\r
delete outerDefinition.style;\r
\r
- var labelId = CKEDITOR.tools.getNextNumber() + '_label';\r
+ var labelId = CKEDITOR.tools.getNextId() + '_label';\r
CKEDITOR.ui.dialog.uiElement.call(\r
this,\r
dialog,\r
if ( elementDefinition.validate )\r
this.validate = elementDefinition.validate;\r
\r
- _.inputId = CKEDITOR.tools.getNextNumber() + '_select';\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
CKEDITOR.tools.htmlEncode( item[0] ) );\r
}\r
\r
+ if (typeof myDefinition.controlStyle != 'undefined')\r
+ myDefinition.style = myDefinition.controlStyle;\r
+\r
_.select = new CKEDITOR.ui.dialog.uiElement( dialog, myDefinition, html, 'select', null, attributes, innerHTML.join( '' ) );\r
return html.join( '' );\r
};\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
* @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
var frameElement = CKEDITOR.document.getById( this._.frameId ),\r
frameDocument = frameElement.getFrameDocument(),\r
elementDefinition = this._.definition,\r
- buttons = this._.buttons;\r
+ buttons = this._.buttons,\r
+ callNumber = this.formLoadedNumber,\r
+ unloadNumber = this.formUnloadNumber,\r
+ langDir = this._.dialog._.editor.lang.dir,\r
+ langCode = this._.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