1 /* 2 Copyright (c) 2003-2009, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 /** @fileoverview The "dialogui" plugin. */ 7 8 CKEDITOR.plugins.add( 'dialogui' ); 9 10 (function() 11 { 12 var initPrivateObject = function( elementDefinition ) 13 { 14 this._ || ( this._ = {} ); 15 this._['default'] = this._.initValue = elementDefinition['default'] || ''; 16 var args = [ this._ ]; 17 for ( var i = 1 ; i < arguments.length ; i++ ) 18 args.push( arguments[i] ); 19 args.push( true ); 20 CKEDITOR.tools.extend.apply( CKEDITOR.tools, args ); 21 return this._; 22 }, 23 textBuilder = 24 { 25 build : function( dialog, elementDefinition, output ) 26 { 27 return new CKEDITOR.ui.dialog.textInput( dialog, elementDefinition, output ); 28 } 29 }, 30 commonBuilder = 31 { 32 build : function( dialog, elementDefinition, output ) 33 { 34 return new CKEDITOR.ui.dialog[elementDefinition.type]( dialog, elementDefinition, output ); 35 } 36 }, 37 commonPrototype = 38 { 39 isChanged : function() 40 { 41 return this.getValue() != this.getInitValue(); 42 }, 43 44 reset : function() 45 { 46 this.setValue( this.getInitValue() ); 47 }, 48 49 setInitValue : function() 50 { 51 this._.initValue = this.getValue(); 52 }, 53 54 resetInitValue : function() 55 { 56 this._.initValue = this._['default']; 57 }, 58 59 getInitValue : function() 60 { 61 return this._.initValue; 62 } 63 }, 64 commonEventProcessors = CKEDITOR.tools.extend( {}, CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors, 65 { 66 onChange : function( dialog, func ) 67 { 68 if ( !this._.domOnChangeRegistered ) 69 { 70 dialog.on( 'load', function() 71 { 72 this.getInputElement().on( 'change', function(){ this.fire( 'change', { value : this.getValue() } ); }, this ); 73 }, this ); 74 this._.domOnChangeRegistered = true; 75 } 76 77 this.on( 'change', func ); 78 } 79 }, true ), 80 eventRegex = /^on([A-Z]\w+)/, 81 cleanInnerDefinition = function( def ) 82 { 83 // An inner UI element should not have the parent's type, title or events. 84 for ( var i in def ) 85 { 86 if ( eventRegex.test( i ) || i == 'title' || i == 'type' ) 87 delete def[i]; 88 } 89 return def; 90 }; 91 92 CKEDITOR.tools.extend( CKEDITOR.ui.dialog, 93 /** @lends CKEDITOR.ui.dialog */ 94 { 95 /** 96 * Base class for all dialog elements with a textual label on the left. 97 * @constructor 98 * @example 99 * @extends CKEDITOR.ui.dialog.uiElement 100 * @param {CKEDITOR.dialog} dialog 101 * Parent dialog object. 102 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 103 * The element definition. Accepted fields: 104 * <ul> 105 * <li><strong>label</strong> (Required) The label string.</li> 106 * <li><strong>labelLayout</strong> (Optional) Put 'horizontal' here if the 107 * label element is to be layed out horizontally. Otherwise a vertical 108 * layout will be used.</li> 109 * <li><strong>widths</strong> (Optional) This applies only for horizontal 110 * layouts - an 2-element array of lengths to specify the widths of the 111 * label and the content element.</li> 112 * </ul> 113 * @param {Array} htmlList 114 * List of HTML code to output to. 115 * @param {Function} contentHtml 116 * A function returning the HTML code string to be added inside the content 117 * cell. 118 */ 119 labeledElement : function( dialog, elementDefinition, htmlList, contentHtml ) 120 { 121 if ( arguments.length < 4 ) 122 return; 123 124 var _ = initPrivateObject.call( this, elementDefinition ); 125 _.labelId = CKEDITOR.tools.getNextNumber() + '_label'; 126 var children = this._.children = []; 127 /** @ignore */ 128 var innerHTML = function() 129 { 130 var html = []; 131 if ( elementDefinition.labelLayout != 'horizontal' ) 132 html.push( '<div class="cke_dialog_ui_labeled_label" id="', 133 _.labelId, 134 '" >', 135 elementDefinition.label, 136 '</div>', 137 '<div class="cke_dialog_ui_labeled_content">', 138 contentHtml( dialog, elementDefinition ), 139 '</div>' ); 140 else 141 { 142 var hboxDefinition = { 143 type : 'hbox', 144 widths : elementDefinition.widths, 145 padding : 0, 146 children : 147 [ 148 { 149 type : 'html', 150 html : '<span class="cke_dialog_ui_labeled_label" ' + 151 'id="' + _.labelId + '">' + CKEDITOR.tools.htmlEncode( elementDefinition.label ) + 152 '</span>' 153 }, 154 { 155 type : 'html', 156 html : '<span class="cke_dialog_ui_labeled_content">' + 157 contentHtml( dialog, elementDefinition ) + 158 '</span>' 159 } 160 ] 161 }; 162 CKEDITOR.dialog._.uiElementBuilders.hbox.build( dialog, hboxDefinition, html ); 163 } 164 return html.join( '' ); 165 }; 166 CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'div', null, null, innerHTML ); 167 }, 168 169 /** 170 * A text input with a label. This UI element class represents both the 171 * single-line text inputs and password inputs in dialog boxes. 172 * @constructor 173 * @example 174 * @extends CKEDITOR.ui.dialog.labeledElement 175 * @param {CKEDITOR.dialog} dialog 176 * Parent dialog object. 177 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 178 * The element definition. Accepted fields: 179 * <ul> 180 * <li><strong>default</strong> (Optional) The default value.</li> 181 * <li><strong>validate</strong> (Optional) The validation function. </li> 182 * <li><strong>maxLength</strong> (Optional) The maximum length of text box 183 * contents.</li> 184 * <li><strong>size</strong> (Optional) The size of the text box. This is 185 * usually overridden by the size defined by the skin, however.</li> 186 * </ul> 187 * @param {Array} htmlList 188 * List of HTML code to output to. 189 */ 190 textInput : function( dialog, elementDefinition, htmlList ) 191 { 192 if ( arguments.length < 3 ) 193 return; 194 195 initPrivateObject.call( this, elementDefinition ); 196 var domId = this._.inputId = CKEDITOR.tools.getNextNumber() + '_textInput', 197 attributes = { 'class' : 'cke_dialog_ui_input_' + elementDefinition.type, id : domId, type : 'text' }, 198 i; 199 200 // Set the validator, if any. 201 if ( elementDefinition.validate ) 202 this.validate = elementDefinition.validate; 203 204 // Set the max length and size. 205 if ( elementDefinition.maxLength ) 206 attributes.maxlength = elementDefinition.maxLength; 207 if ( elementDefinition.size ) 208 attributes.size = elementDefinition.size; 209 210 // If user presses Enter in a text box, it implies clicking OK for the dialog. 211 var me = this, keyPressedOnMe = false; 212 dialog.on( 'load', function() 213 { 214 me.getInputElement().on( 'keydown', function( evt ) 215 { 216 if ( evt.data.getKeystroke() == 13 ) 217 keyPressedOnMe = true; 218 } ); 219 220 // Lower the priority this 'keyup' since 'ok' will close the dialog.(#3749) 221 me.getInputElement().on( 'keyup', function( evt ) 222 { 223 if ( evt.data.getKeystroke() == 13 && keyPressedOnMe ) 224 { 225 dialog.getButton( 'ok' ) && dialog.getButton( 'ok' ).click(); 226 keyPressedOnMe = false; 227 } 228 }, null, null, 1000 ); 229 } ); 230 231 /** @ignore */ 232 var innerHTML = function() 233 { 234 // IE BUG: Text input fields in IE at 100% would exceed a <td> or inline 235 // container's width, so need to wrap it inside a <div>. 236 var html = [ '<div class="cke_dialog_ui_input_', elementDefinition.type, '"><input ' ]; 237 for ( var i in attributes ) 238 html.push( i + '="' + attributes[i] + '" ' ); 239 html.push( ' /></div>' ); 240 return html.join( '' ); 241 }; 242 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); 243 }, 244 245 /** 246 * A text area with a label on the top or left. 247 * @constructor 248 * @extends CKEDITOR.ui.dialog.labeledElement 249 * @example 250 * @param {CKEDITOR.dialog} dialog 251 * Parent dialog object. 252 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 253 * The element definition. Accepted fields: 254 * <ul> 255 * <li><strong>rows</strong> (Optional) The number of rows displayed. 256 * Defaults to 5 if not defined.</li> 257 * <li><strong>cols</strong> (Optional) The number of cols displayed. 258 * Defaults to 20 if not defined. Usually overridden by skins.</li> 259 * <li><strong>default</strong> (Optional) The default value.</li> 260 * <li><strong>validate</strong> (Optional) The validation function. </li> 261 * </ul> 262 * @param {Array} htmlList 263 * List of HTML code to output to. 264 */ 265 textarea : function( dialog, elementDefinition, htmlList ) 266 { 267 if ( arguments.length < 3 ) 268 return; 269 270 initPrivateObject.call( this, elementDefinition ); 271 var me = this, 272 domId = this._.inputId = CKEDITOR.tools.getNextNumber() + '_textarea', 273 attributes = {}; 274 275 if ( elementDefinition.validate ) 276 this.validate = elementDefinition.validate; 277 278 // Generates the essential attributes for the textarea tag. 279 attributes.rows = elementDefinition.rows || 5; 280 attributes.cols = elementDefinition.cols || 20; 281 282 /** @ignore */ 283 var innerHTML = function() 284 { 285 var html = [ '<div class="cke_dialog_ui_input_textarea"><textarea class="cke_dialog_ui_input_textarea" id="', domId, '" ' ]; 286 for ( var i in attributes ) 287 html.push( i + '="' + CKEDITOR.tools.htmlEncode( attributes[i] ) + '" ' ); 288 html.push( '>', CKEDITOR.tools.htmlEncode( me._['default'] ), '</textarea></div>' ); 289 return html.join( '' ); 290 }; 291 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); 292 }, 293 294 /** 295 * A single checkbox with a label on the right. 296 * @constructor 297 * @extends CKEDITOR.ui.dialog.uiElement 298 * @example 299 * @param {CKEDITOR.dialog} dialog 300 * Parent dialog object. 301 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 302 * The element definition. Accepted fields: 303 * <ul> 304 * <li><strong>checked</strong> (Optional) Whether the checkbox is checked 305 * on instantiation. Defaults to false.</li> 306 * <li><strong>validate</strong> (Optional) The validation function.</li> 307 * <li><strong>label</strong> (Optional) The checkbox label.</li> 308 * </ul> 309 * @param {Array} htmlList 310 * List of HTML code to output to. 311 */ 312 checkbox : function( dialog, elementDefinition, htmlList ) 313 { 314 if ( arguments.length < 3 ) 315 return; 316 317 var _ = initPrivateObject.call( this, elementDefinition, { 'default' : !!elementDefinition[ 'default' ] } ); 318 319 if ( elementDefinition.validate ) 320 this.validate = elementDefinition.validate; 321 322 /** @ignore */ 323 var innerHTML = function() 324 { 325 var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition, 326 { 327 id : elementDefinition.id ? elementDefinition.id + '_checkbox' : CKEDITOR.tools.getNextNumber() + '_checkbox' 328 }, true ), 329 html = [], 330 attributes = { 'class' : 'cke_dialog_ui_checkbox_input', type : 'checkbox' }; 331 cleanInnerDefinition( myDefinition ); 332 if ( elementDefinition[ 'default' ] ) 333 attributes.checked = 'checked'; 334 _.checkbox = new CKEDITOR.ui.dialog.uiElement( dialog, myDefinition, html, 'input', null, attributes ); 335 html.push( ' ', CKEDITOR.tools.htmlEncode( elementDefinition.label ) ); 336 return html.join( '' ); 337 }; 338 339 CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'label', null, null, innerHTML ); 340 }, 341 342 /** 343 * A group of radio buttons. 344 * @constructor 345 * @example 346 * @extends CKEDITOR.ui.dialog.labeledElement 347 * @param {CKEDITOR.dialog} dialog 348 * Parent dialog object. 349 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 350 * The element definition. Accepted fields: 351 * <ul> 352 * <li><strong>default</strong> (Required) The default value.</li> 353 * <li><strong>validate</strong> (Optional) The validation function.</li> 354 * <li><strong>items</strong> (Required) An array of options. Each option 355 * is a 1- or 2-item array of format [ 'Description', 'Value' ]. If 'Value' 356 * is missing, then the value would be assumed to be the same as the 357 * description.</li> 358 * </ul> 359 * @param {Array} htmlList 360 * List of HTML code to output to. 361 */ 362 radio : function( dialog, elementDefinition, htmlList ) 363 { 364 if ( arguments.length < 3) 365 return; 366 367 initPrivateObject.call( this, elementDefinition ); 368 if ( !this._['default'] ) 369 this._['default'] = this._.initValue = elementDefinition.items[0][1]; 370 if ( elementDefinition.validate ) 371 this.validate = elementDefinition.valdiate; 372 var children = [], me = this; 373 374 /** @ignore */ 375 var innerHTML = function() 376 { 377 var inputHtmlList = [], html = [], 378 commonAttributes = { 'class' : 'cke_dialog_ui_radio_item' }, 379 commonName = elementDefinition.id ? elementDefinition.id + '_radio' : CKEDITOR.tools.getNextNumber() + '_radio'; 380 for ( var i = 0 ; i < elementDefinition.items.length ; i++ ) 381 { 382 var item = elementDefinition.items[i], 383 title = item[2] !== undefined ? item[2] : item[0], 384 value = item[1] !== undefined ? item[1] : item[0], 385 inputDefinition = CKEDITOR.tools.extend( {}, elementDefinition, 386 { 387 id : CKEDITOR.tools.getNextNumber() + '_radio_input', 388 title : null, 389 type : null 390 }, true ), 391 labelDefinition = CKEDITOR.tools.extend( {}, inputDefinition, 392 { 393 id : null, 394 title : title 395 }, true ), 396 inputHtml = [], 397 inputAttributes = 398 { 399 type : 'radio', 400 'class' : 'cke_dialog_ui_radio_input', 401 name : commonName, 402 value : value 403 }; 404 if ( me._['default'] == value ) 405 inputAttributes.checked = 'checked'; 406 cleanInnerDefinition( inputDefinition ); 407 cleanInnerDefinition( labelDefinition ); 408 children.push( new CKEDITOR.ui.dialog.uiElement( dialog, inputDefinition, inputHtml, 'input', null, inputAttributes ) ); 409 new CKEDITOR.ui.dialog.uiElement( dialog, labelDefinition, inputHtmlList, 'label', null, null, 410 inputHtml.join( '' ) + ' ' + item[0] ); 411 } 412 new CKEDITOR.ui.dialog.hbox( dialog, [], inputHtmlList, html ); 413 return html.join( '' ); 414 }; 415 416 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); 417 this._.children = children; 418 }, 419 420 /** 421 * A button with a label inside. 422 * @constructor 423 * @example 424 * @extends CKEDITOR.ui.dialog.uiElement 425 * @param {CKEDITOR.dialog} dialog 426 * Parent dialog object. 427 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 428 * The element definition. Accepted fields: 429 * <ul> 430 * <li><strong>label</strong> (Required) The button label.</li> 431 * <li><strong>disabled</strong> (Optional) Set to true if you want the 432 * button to appear in disabled state.</li> 433 * </ul> 434 * @param {Array} htmlList 435 * List of HTML code to output to. 436 */ 437 button : function( dialog, elementDefinition, htmlList ) 438 { 439 if ( !arguments.length ) 440 return; 441 442 if ( typeof elementDefinition == 'function' ) 443 elementDefinition = elementDefinition( dialog.getParentEditor() ); 444 445 initPrivateObject.call( this, elementDefinition, { disabled : elementDefinition.disabled || false } ); 446 447 // Add OnClick event to this input. 448 CKEDITOR.event.implementOn( this ); 449 450 var me = this; 451 452 // Register an event handler for processing button clicks. 453 dialog.on( 'load', function( eventInfo ) 454 { 455 var element = this.getElement(); 456 457 (function() 458 { 459 element.on( 'click', function( evt ) 460 { 461 me.fire( 'click', { dialog : me.getDialog() } ); 462 evt.data.preventDefault(); 463 } ); 464 })(); 465 466 element.unselectable(); 467 }, this ); 468 469 var outerDefinition = CKEDITOR.tools.extend( {}, elementDefinition ); 470 delete outerDefinition.style; 471 472 CKEDITOR.ui.dialog.uiElement.call( 473 this, 474 dialog, 475 outerDefinition, 476 htmlList, 477 'a', 478 null, 479 { 480 style : elementDefinition.style, 481 href : 'javascript:void(0)', 482 title : elementDefinition.label, 483 hidefocus : 'true', 484 'class' : elementDefinition['class'] 485 }, 486 '<span class="cke_dialog_ui_button">' + 487 CKEDITOR.tools.htmlEncode( elementDefinition.label ) + 488 '</span>' ); 489 }, 490 491 /** 492 * A select box. 493 * @extends CKEDITOR.ui.dialog.uiElement 494 * @example 495 * @constructor 496 * @param {CKEDITOR.dialog} dialog 497 * Parent dialog object. 498 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 499 * The element definition. Accepted fields: 500 * <ul> 501 * <li><strong>default</strong> (Required) The default value.</li> 502 * <li><strong>validate</strong> (Optional) The validation function.</li> 503 * <li><strong>items</strong> (Required) An array of options. Each option 504 * is a 1- or 2-item array of format [ 'Description', 'Value' ]. If 'Value' 505 * is missing, then the value would be assumed to be the same as the 506 * description.</li> 507 * <li><strong>multiple</strong> (Optional) Set this to true if you'd like 508 * to have a multiple-choice select box.</li> 509 * <li><strong>size</strong> (Optional) The number of items to display in 510 * the select box.</li> 511 * </ul> 512 * @param {Array} htmlList 513 * List of HTML code to output to. 514 */ 515 select : function( dialog, elementDefinition, htmlList ) 516 { 517 if ( arguments.length < 3 ) 518 return; 519 520 var _ = initPrivateObject.call( this, elementDefinition ); 521 522 if ( elementDefinition.validate ) 523 this.validate = elementDefinition.validate; 524 525 /** @ignore */ 526 var innerHTML = function() 527 { 528 var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition, 529 { 530 id : elementDefinition.id ? elementDefinition.id + '_select' : CKEDITOR.tools.getNextNumber() + '_select' 531 }, true ), 532 html = [], 533 innerHTML = [], 534 attributes = { 'class' : 'cke_dialog_ui_input_select' }; 535 536 // Add multiple and size attributes from element definition. 537 if ( elementDefinition.size != undefined ) 538 attributes.size = elementDefinition.size; 539 if ( elementDefinition.multiple != undefined ) 540 attributes.multiple = elementDefinition.multiple; 541 542 cleanInnerDefinition( myDefinition ); 543 for ( var i = 0, item ; i < elementDefinition.items.length && ( item = elementDefinition.items[i] ) ; i++ ) 544 { 545 innerHTML.push( '<option value="', 546 CKEDITOR.tools.htmlEncode( item[1] !== undefined ? item[1] : item[0] ), '" /> ', 547 CKEDITOR.tools.htmlEncode( item[0] ) ); 548 } 549 550 _.select = new CKEDITOR.ui.dialog.uiElement( dialog, myDefinition, html, 'select', null, attributes, innerHTML.join( '' ) ); 551 return html.join( '' ); 552 }; 553 554 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); 555 }, 556 557 /** 558 * A file upload input. 559 * @extends CKEDITOR.ui.dialog.labeledElement 560 * @example 561 * @constructor 562 * @param {CKEDITOR.dialog} dialog 563 * Parent dialog object. 564 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 565 * The element definition. Accepted fields: 566 * <ul> 567 * <li><strong>validate</strong> (Optional) The validation function.</li> 568 * </ul> 569 * @param {Array} htmlList 570 * List of HTML code to output to. 571 */ 572 file : function( dialog, elementDefinition, htmlList ) 573 { 574 if ( arguments.length < 3 ) 575 return; 576 577 if ( elementDefinition['default'] === undefined ) 578 elementDefinition['default'] = ''; 579 580 var _ = CKEDITOR.tools.extend( initPrivateObject.call( this, elementDefinition ), { definition : elementDefinition, buttons : [] } ); 581 582 if ( elementDefinition.validate ) 583 this.validate = elementDefinition.validate; 584 585 /** @ignore */ 586 var innerHTML = function() 587 { 588 _.frameId = CKEDITOR.tools.getNextNumber() + '_fileInput'; 589 590 // Support for custom document.domain in IE. 591 var isCustomDomain = CKEDITOR.env.ie && document.domain != window.location.hostname; 592 593 var html = [ 594 '<iframe' + 595 ' frameborder="0"' + 596 ' allowtransparency="0"' + 597 ' class="cke_dialog_ui_input_file"' + 598 ' id="', _.frameId, '"' + 599 ' src="javascript:void(' ]; 600 601 html.push( 602 isCustomDomain ? 603 '(function(){' + 604 'document.open();' + 605 'document.domain=\'' + document.domain + '\';' + 606 'document.close();' + 607 '})()' 608 : 609 '0' ); 610 611 html.push( 612 ')">' + 613 '</iframe>' ); 614 615 return html.join( '' ); 616 }; 617 618 // IE BUG: Parent container does not resize to contain the iframe automatically. 619 dialog.on( 'load', function() 620 { 621 var iframe = CKEDITOR.document.getById( _.frameId ), 622 contentDiv = iframe.getParent(); 623 contentDiv.addClass( 'cke_dialog_ui_input_file' ); 624 } ); 625 626 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); 627 }, 628 629 /** 630 * A button for submitting the file in a file upload input. 631 * @extends CKEDITOR.ui.dialog.button 632 * @example 633 * @constructor 634 * @param {CKEDITOR.dialog} dialog 635 * Parent dialog object. 636 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 637 * The element definition. Accepted fields: 638 * <ul> 639 * <li><strong>for</strong> (Required) The file input's page and element Id 640 * to associate to, in a 2-item array format: [ 'page_id', 'element_id' ]. 641 * </li> 642 * <li><strong>validate</strong> (Optional) The validation function.</li> 643 * </ul> 644 * @param {Array} htmlList 645 * List of HTML code to output to. 646 */ 647 fileButton : function( dialog, elementDefinition, htmlList ) 648 { 649 if ( arguments.length < 3 ) 650 return; 651 652 var _ = initPrivateObject.call( this, elementDefinition ), 653 me = this; 654 655 if ( elementDefinition.validate ) 656 this.validate = elementDefinition.validate; 657 658 var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition ); 659 var onClick = myDefinition.onClick; 660 myDefinition.className = ( myDefinition.className ? myDefinition.className + ' ' : '' ) + 'cke_dialog_ui_button'; 661 myDefinition.onClick = function( evt ) 662 { 663 var target = elementDefinition[ 'for' ]; // [ pageId, elementId ] 664 if ( !onClick || onClick.call( this, evt ) !== false ) 665 { 666 dialog.getContentElement( target[0], target[1] ).submit(); 667 this.disable(); 668 } 669 }; 670 671 dialog.on( 'load', function() 672 { 673 dialog.getContentElement( elementDefinition[ 'for' ][0], elementDefinition[ 'for' ][1] )._.buttons.push( me ); 674 } ); 675 676 CKEDITOR.ui.dialog.button.call( this, dialog, myDefinition, htmlList ); 677 }, 678 679 html : (function() 680 { 681 var myHtmlRe = /^\s*<[\w:]+\s+([^>]*)?>/, 682 theirHtmlRe = /^(\s*<[\w:]+(?:\s+[^>]*)?)((?:.|\r|\n)+)$/, 683 emptyTagRe = /\/$/; 684 /** 685 * A dialog element made from raw HTML code. 686 * @extends CKEDITOR.ui.dialog.uiElement 687 * @name CKEDITOR.ui.dialog.html 688 * @param {CKEDITOR.dialog} dialog Parent dialog object. 689 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition Element definition. 690 * Accepted fields: 691 * <ul> 692 * <li><strong>html</strong> (Required) HTML code of this element.</li> 693 * </ul> 694 * @param {Array} htmlList List of HTML code to be added to the dialog's content area. 695 * @example 696 * @constructor 697 */ 698 return function( dialog, elementDefinition, htmlList ) 699 { 700 if ( arguments.length < 3 ) 701 return; 702 703 var myHtmlList = [], 704 myHtml, 705 theirHtml = elementDefinition.html, 706 myMatch, theirMatch; 707 708 // If the HTML input doesn't contain any tags at the beginning, add a <span> tag around it. 709 if ( theirHtml.charAt( 0 ) != '<' ) 710 theirHtml = '<span>' + theirHtml + '</span>'; 711 712 // Look for focus function in definition. 713 if ( elementDefinition.focus ) 714 { 715 var oldFocus = this.focus; 716 this.focus = function() 717 { 718 oldFocus.call( this ); 719 elementDefinition.focus.call( this ); 720 this.fire( 'focus' ); 721 }; 722 if ( elementDefinition.isFocusable ) 723 { 724 var oldIsFocusable = this.isFocusable; 725 this.isFocusable = oldIsFocusable; 726 } 727 this.keyboardFocusable = true; 728 } 729 730 CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, myHtmlList, 'span', null, null, '' ); 731 732 // Append the attributes created by the uiElement call to the real HTML. 733 myHtml = myHtmlList.join( '' ); 734 myMatch = myHtml.match( myHtmlRe ); 735 theirMatch = theirHtml.match( theirHtmlRe ) || [ '', '', '' ]; 736 737 if ( emptyTagRe.test( theirMatch[1] ) ) 738 { 739 theirMatch[1] = theirMatch[1].slice( 0, -1 ); 740 theirMatch[2] = '/' + theirMatch[2]; 741 } 742 743 htmlList.push( [ theirMatch[1], ' ', myMatch[1] || '', theirMatch[2] ].join( '' ) ); 744 }; 745 })() 746 }, true ); 747 748 CKEDITOR.ui.dialog.html.prototype = new CKEDITOR.ui.dialog.uiElement; 749 750 CKEDITOR.ui.dialog.labeledElement.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, 751 /** @lends CKEDITOR.ui.dialog.labeledElement.prototype */ 752 { 753 /** 754 * Sets the label text of the element. 755 * @param {String} label The new label text. 756 * @returns {CKEDITOR.ui.dialog.labeledElement} The current labeled element. 757 * @example 758 */ 759 setLabel : function( label ) 760 { 761 var node = CKEDITOR.document.getById( this._.labelId ); 762 if ( node.getChildCount() < 1 ) 763 ( new CKEDITOR.dom.text( label, CKEDITOR.document ) ).appendTo( node ); 764 else 765 node.getChild( 0 ).$.nodeValue = label; 766 return this; 767 }, 768 769 /** 770 * Retrieves the current label text of the elment. 771 * @returns {String} The current label text. 772 * @example 773 */ 774 getLabel : function() 775 { 776 var node = CKEDITOR.document.getById( this._.labelId ); 777 if ( !node || node.getChildCount() < 1 ) 778 return ''; 779 else 780 return node.getChild( 0 ).getText(); 781 }, 782 783 /** 784 * Defines the onChange event for UI element definitions. 785 * @field 786 * @type Object 787 * @example 788 */ 789 eventProcessors : commonEventProcessors 790 }, true ); 791 792 CKEDITOR.ui.dialog.button.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, 793 /** @lends CKEDITOR.ui.dialog.button.prototype */ 794 { 795 /** 796 * Simulates a click to the button. 797 * @example 798 * @returns {Object} Return value of the 'click' event. 799 */ 800 click : function() 801 { 802 if ( !this._.disabled ) 803 return this.fire( 'click', { dialog : this._.dialog } ); 804 this.getElement().$.blur(); 805 return false; 806 }, 807 808 /** 809 * Enables the button. 810 * @example 811 */ 812 enable : function() 813 { 814 this._.disabled = false; 815 this.getElement().removeClass( 'disabled' ); 816 }, 817 818 /** 819 * Disables the button. 820 * @example 821 */ 822 disable : function() 823 { 824 this._.disabled = true; 825 this.getElement().addClass( 'disabled' ); 826 }, 827 828 isVisible : function() 829 { 830 return !!this.getElement().$.firstChild.offsetHeight; 831 }, 832 833 isEnabled : function() 834 { 835 return !this._.disabled; 836 }, 837 838 /** 839 * Defines the onChange event and onClick for button element definitions. 840 * @field 841 * @type Object 842 * @example 843 */ 844 eventProcessors : CKEDITOR.tools.extend( {}, CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors, 845 { 846 /** @ignore */ 847 onClick : function( dialog, func ) 848 { 849 this.on( 'click', func ); 850 } 851 }, true ), 852 853 /** 854 * Handler for the element's access key up event. Simulates a click to 855 * the button. 856 * @example 857 */ 858 accessKeyUp : function() 859 { 860 this.click(); 861 }, 862 863 /** 864 * Handler for the element's access key down event. Simulates a mouse 865 * down to the button. 866 * @example 867 */ 868 accessKeyDown : function() 869 { 870 this.focus(); 871 }, 872 873 keyboardFocusable : true 874 }, true ); 875 876 CKEDITOR.ui.dialog.textInput.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement, 877 /** @lends CKEDITOR.ui.dialog.textInput.prototype */ 878 { 879 /** 880 * Gets the text input DOM element under this UI object. 881 * @example 882 * @returns {CKEDITOR.dom.element} The DOM element of the text input. 883 */ 884 getInputElement : function() 885 { 886 return CKEDITOR.document.getById( this._.inputId ); 887 }, 888 889 /** 890 * Puts focus into the text input. 891 * @example 892 */ 893 focus : function() 894 { 895 var me = this.selectParentTab(); 896 897 // GECKO BUG: setTimeout() is needed to workaround invisible selections. 898 setTimeout( function(){ me.getInputElement().$.focus(); }, 0 ); 899 }, 900 901 /** 902 * Selects all the text in the text input. 903 * @example 904 */ 905 select : function() 906 { 907 var me = this.selectParentTab(); 908 909 // GECKO BUG: setTimeout() is needed to workaround invisible selections. 910 setTimeout( function(){ var e = me.getInputElement().$; e.focus(); e.select(); }, 0 ); 911 }, 912 913 /** 914 * Handler for the text input's access key up event. Makes a select() 915 * call to the text input. 916 * @example 917 */ 918 accessKeyUp : function() 919 { 920 this.select(); 921 }, 922 923 /** 924 * Sets the value of this text input object. 925 * @param {Object} value The new value. 926 * @returns {CKEDITOR.ui.dialog.textInput} The current UI element. 927 * @example 928 * uiElement.setValue( 'Blamo' ); 929 */ 930 setValue : function( value ) 931 { 932 value = value || ''; 933 return CKEDITOR.ui.dialog.uiElement.prototype.setValue.call( this, value ); 934 }, 935 936 keyboardFocusable : true 937 }, commonPrototype, true ); 938 939 CKEDITOR.ui.dialog.textarea.prototype = new CKEDITOR.ui.dialog.textInput(); 940 941 CKEDITOR.ui.dialog.select.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement, 942 /** @lends CKEDITOR.ui.dialog.select.prototype */ 943 { 944 /** 945 * Gets the DOM element of the select box. 946 * @returns {CKEDITOR.dom.element} The <select> element of this UI 947 * element. 948 * @example 949 */ 950 getInputElement : function() 951 { 952 return this._.select.getElement(); 953 }, 954 955 /** 956 * Adds an option to the select box. 957 * @param {String} label Option label. 958 * @param {String} value (Optional) Option value, if not defined it'll be 959 * assumed to be the same as the label. 960 * @param {Number} index (Optional) Position of the option to be inserted 961 * to. If not defined the new option will be inserted to the end of list. 962 * @example 963 * @returns {CKEDITOR.ui.dialog.select} The current select UI element. 964 */ 965 add : function( label, value, index ) 966 { 967 var option = new CKEDITOR.dom.element( 'option', this.getDialog().getParentEditor().document ), 968 selectElement = this.getInputElement().$; 969 option.$.text = label; 970 option.$.value = ( value === undefined || value === null ) ? label : value; 971 if ( index === undefined || index === null ) 972 { 973 if ( CKEDITOR.env.ie ) 974 selectElement.add( option.$ ); 975 else 976 selectElement.add( option.$, null ); 977 } 978 else 979 selectElement.add( option.$, index ); 980 return this; 981 }, 982 983 /** 984 * Removes an option from the selection list. 985 * @param {Number} index Index of the option to be removed. 986 * @example 987 * @returns {CKEDITOR.ui.dialog.select} The current select UI element. 988 */ 989 remove : function( index ) 990 { 991 var selectElement = this.getInputElement().$; 992 selectElement.remove( index ); 993 return this; 994 }, 995 996 /** 997 * Clears all options out of the selection list. 998 * @returns {CKEDITOR.ui.dialog.select} The current select UI element. 999 */ 1000 clear : function() 1001 { 1002 var selectElement = this.getInputElement().$; 1003 while ( selectElement.length > 0 ) 1004 selectElement.remove( 0 ); 1005 return this; 1006 }, 1007 1008 keyboardFocusable : true 1009 }, commonPrototype, true ); 1010 1011 CKEDITOR.ui.dialog.checkbox.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, 1012 /** @lends CKEDITOR.ui.dialog.checkbox.prototype */ 1013 { 1014 /** 1015 * Gets the checkbox DOM element. 1016 * @example 1017 * @returns {CKEDITOR.dom.element} The DOM element of the checkbox. 1018 */ 1019 getInputElement : function() 1020 { 1021 return this._.checkbox.getElement(); 1022 }, 1023 1024 /** 1025 * Sets the state of the checkbox. 1026 * @example 1027 * @param {Boolean} true to tick the checkbox, false to untick it. 1028 */ 1029 setValue : function( checked ) 1030 { 1031 this.getInputElement().$.checked = checked; 1032 this.fire( 'change', { value : checked } ); 1033 }, 1034 1035 /** 1036 * Gets the state of the checkbox. 1037 * @example 1038 * @returns {Boolean} true means the checkbox is ticked, false means it's not ticked. 1039 */ 1040 getValue : function() 1041 { 1042 return this.getInputElement().$.checked; 1043 }, 1044 1045 /** 1046 * Handler for the access key up event. Toggles the checkbox. 1047 * @example 1048 */ 1049 accessKeyUp : function() 1050 { 1051 this.setValue( !this.getValue() ); 1052 }, 1053 1054 /** 1055 * Defines the onChange event for UI element definitions. 1056 * @field 1057 * @type Object 1058 * @example 1059 */ 1060 eventProcessors : 1061 { 1062 onChange : function( dialog, func ) 1063 { 1064 if ( !CKEDITOR.env.ie ) 1065 return commonEventProcessors.onChange.apply( this, arguments ); 1066 else 1067 { 1068 dialog.on( 'load', function() 1069 { 1070 var element = this._.checkbox.getElement(); 1071 element.on( 'propertychange', function( evt ) 1072 { 1073 evt = evt.data.$; 1074 if ( evt.propertyName == 'checked' ) 1075 this.fire( 'change', { value : element.$.checked } ); 1076 }, this ); 1077 }, this ); 1078 this.on( 'change', func ); 1079 } 1080 return null; 1081 } 1082 }, 1083 1084 keyboardFocusable : true 1085 }, commonPrototype, true ); 1086 1087 CKEDITOR.ui.dialog.radio.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, 1088 /** @lends CKEDITOR.ui.dialog.radio.prototype */ 1089 { 1090 /** 1091 * Checks one of the radio buttons in this button group. 1092 * @example 1093 * @param {String} value The value of the button to be chcked. 1094 */ 1095 setValue : function( value ) 1096 { 1097 var children = this._.children, 1098 item; 1099 for ( var i = 0 ; ( i < children.length ) && ( item = children[i] ) ; i++ ) 1100 item.getElement().$.checked = ( item.getValue() == value ); 1101 this.fire( 'change', { value : value } ); 1102 }, 1103 1104 /** 1105 * Gets the value of the currently checked radio button. 1106 * @example 1107 * @returns {String} The currently checked button's value. 1108 */ 1109 getValue : function() 1110 { 1111 var children = this._.children; 1112 for ( var i = 0 ; i < children.length ; i++ ) 1113 { 1114 if ( children[i].getElement().$.checked ) 1115 return children[i].getValue(); 1116 } 1117 return null; 1118 }, 1119 1120 /** 1121 * Handler for the access key up event. Focuses the currently 1122 * selected radio button, or the first radio button if none is 1123 * selected. 1124 * @example 1125 */ 1126 accessKeyUp : function() 1127 { 1128 var children = this._.children, i; 1129 for ( i = 0 ; i < children.length ; i++ ) 1130 { 1131 if ( children[i].getElement().$.checked ) 1132 { 1133 children[i].getElement().focus(); 1134 return; 1135 } 1136 } 1137 children[0].getElement().focus(); 1138 }, 1139 1140 /** 1141 * Defines the onChange event for UI element definitions. 1142 * @field 1143 * @type Object 1144 * @example 1145 */ 1146 eventProcessors : 1147 { 1148 onChange : function( dialog, func ) 1149 { 1150 if ( !CKEDITOR.env.ie ) 1151 return commonEventProcessors.onChange.apply( this, arguments ); 1152 else 1153 { 1154 dialog.on( 'load', function() 1155 { 1156 var children = this._.children, me = this; 1157 for ( var i = 0 ; i < children.length ; i++ ) 1158 { 1159 var element = children[i].getElement(); 1160 element.on( 'propertychange', function( evt ) 1161 { 1162 evt = evt.data.$; 1163 if ( evt.propertyName == 'checked' && this.$.checked ) 1164 me.fire( 'change', { value : this.getAttribute( 'value' ) } ); 1165 } ); 1166 } 1167 }, this ); 1168 this.on( 'change', func ); 1169 } 1170 return null; 1171 } 1172 }, 1173 1174 keyboardFocusable : true 1175 }, commonPrototype, true ); 1176 1177 CKEDITOR.ui.dialog.file.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement, 1178 commonPrototype, 1179 /** @lends CKEDITOR.ui.dialog.file.prototype */ 1180 { 1181 /** 1182 * Gets the <input> element of this file input. 1183 * @returns {CKEDITOR.dom.element} The file input element. 1184 * @example 1185 */ 1186 getInputElement : function() 1187 { 1188 var frameDocument = CKEDITOR.document.getById( this._.frameId ).getFrameDocument(); 1189 return frameDocument.$.forms.length > 0 ? 1190 new CKEDITOR.dom.element( frameDocument.$.forms[0].elements[0] ) : 1191 this.getElement(); 1192 }, 1193 1194 /** 1195 * Uploads the file in the file input. 1196 * @returns {CKEDITOR.ui.dialog.file} This object. 1197 * @example 1198 */ 1199 submit : function() 1200 { 1201 this.getInputElement().getParent().$.submit(); 1202 return this; 1203 }, 1204 1205 /** 1206 * Get the action assigned to the form. 1207 * @returns {String} The value of the action. 1208 * @example 1209 */ 1210 getAction : function( action ) 1211 { 1212 return this.getInputElement().getParent().$.action; 1213 }, 1214 1215 /** 1216 * Redraws the file input and resets the file path in the file input. 1217 * The redraw logic is necessary because non-IE browsers tend to clear 1218 * the <iframe> containing the file input after closing the dialog. 1219 * @example 1220 */ 1221 reset : function() 1222 { 1223 var frameElement = CKEDITOR.document.getById( this._.frameId ), 1224 frameDocument = frameElement.getFrameDocument(), 1225 elementDefinition = this._.definition, 1226 buttons = this._.buttons; 1227 1228 function generateFormField() 1229 { 1230 frameDocument.$.open(); 1231 1232 // Support for custom document.domain in IE. 1233 if ( CKEDITOR.env.isCustomDomain() ) 1234 frameDocument.$.domain = document.domain; 1235 1236 frameDocument.$.write( [ '<html><head><title></title></head><body style="margin: 0; overflow: hidden; background: transparent;">', 1237 '<form enctype="multipart/form-data" method="POST" action="', 1238 CKEDITOR.tools.htmlEncode( elementDefinition.action ), 1239 '">', 1240 '<input type="file" name="', 1241 CKEDITOR.tools.htmlEncode( elementDefinition.id || 'cke_upload' ), 1242 '" size="', 1243 CKEDITOR.tools.htmlEncode( elementDefinition.size || '' ), 1244 '" />', 1245 '</form>', 1246 '</body></html>' ].join( '' ) ); 1247 1248 frameDocument.$.close(); 1249 1250 for ( var i = 0 ; i < buttons.length ; i++ ) 1251 buttons[i].enable(); 1252 } 1253 1254 // #3465: Wait for the browser to finish rendering the dialog first. 1255 if ( CKEDITOR.env.gecko ) 1256 setTimeout( generateFormField, 500 ); 1257 else 1258 generateFormField(); 1259 }, 1260 1261 getValue : function() 1262 { 1263 // The file path returned from the input tag is incomplete anyway, so it's 1264 // safe to ignore it and prevent the confirmation dialog from appearing. 1265 // (Part of #3465) 1266 return ''; 1267 }, 1268 1269 /** 1270 * Defines the onChange event for UI element definitions. 1271 * @field 1272 * @type Object 1273 * @example 1274 */ 1275 eventProcessors : commonEventProcessors, 1276 1277 keyboardFocusable : true 1278 }, true ); 1279 1280 CKEDITOR.ui.dialog.fileButton.prototype = new CKEDITOR.ui.dialog.button; 1281 1282 CKEDITOR.dialog.addUIElement( 'text', textBuilder ); 1283 CKEDITOR.dialog.addUIElement( 'password', textBuilder ); 1284 CKEDITOR.dialog.addUIElement( 'textarea', commonBuilder ); 1285 CKEDITOR.dialog.addUIElement( 'checkbox', commonBuilder ); 1286 CKEDITOR.dialog.addUIElement( 'radio', commonBuilder ); 1287 CKEDITOR.dialog.addUIElement( 'button', commonBuilder ); 1288 CKEDITOR.dialog.addUIElement( 'select', commonBuilder ); 1289 CKEDITOR.dialog.addUIElement( 'file', commonBuilder ); 1290 CKEDITOR.dialog.addUIElement( 'fileButton', commonBuilder ); 1291 CKEDITOR.dialog.addUIElement( 'html', commonBuilder ); 1292 })(); 1293