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 CKEDITOR.tools.htmlEncode( 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 }, 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 me.getInputElement().on( 'keyup', function( evt ) 220 { 221 if ( evt.data.getKeystroke() == 13 && keyPressedOnMe ) 222 { 223 dialog.getButton( 'ok' ) && dialog.getButton( 'ok' ).click(); 224 keyPressedOnMe = false; 225 } 226 } ); 227 } ); 228 229 /** @ignore */ 230 var innerHTML = function() 231 { 232 // IE BUG: Text input fields in IE at 100% would exceed a <td> or inline 233 // container's width, so need to wrap it inside a <div>. 234 var html = [ '<div class="cke_dialog_ui_input_', elementDefinition.type, '"><input ' ]; 235 for ( var i in attributes ) 236 html.push( i + '="' + attributes[i] + '" ' ); 237 html.push( ' /></div>' ); 238 return html.join( '' ); 239 }; 240 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); 241 }, 242 243 /** 244 * A text area with a label on the top or left. 245 * @constructor 246 * @extends CKEDITOR.ui.dialog.labeledElement 247 * @example 248 * @param {CKEDITOR.dialog} dialog 249 * Parent dialog object. 250 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 251 * The element definition. Accepted fields: 252 * <ul> 253 * <li><strong>rows</strong> (Optional) The number of rows displayed. 254 * Defaults to 5 if not defined.</li> 255 * <li><strong>cols</strong> (Optional) The number of cols displayed. 256 * Defaults to 20 if not defined. Usually overridden by skins.</li> 257 * <li><strong>default</strong> (Optional) The default value.</li> 258 * <li><strong>validate</strong> (Optional) The validation function. </li> 259 * </ul> 260 * @param {Array} htmlList 261 * List of HTML code to output to. 262 */ 263 textarea : function( dialog, elementDefinition, htmlList ) 264 { 265 if ( arguments.length < 3 ) 266 return; 267 268 initPrivateObject.call( this, elementDefinition ); 269 var me = this, 270 domId = this._.inputId = CKEDITOR.tools.getNextNumber() + '_textarea', 271 attributes = {}; 272 273 if ( elementDefinition.validate ) 274 this.validate = elementDefinition.validate; 275 276 // Generates the essential attributes for the textarea tag. 277 attributes.rows = elementDefinition.rows || 5; 278 attributes.cols = elementDefinition.cols || 20; 279 280 /** @ignore */ 281 var innerHTML = function() 282 { 283 var html = [ '<div class="cke_dialog_ui_input_textarea"><textarea class="cke_dialog_ui_input_textarea" id="', domId, '" ' ]; 284 for ( var i in attributes ) 285 html.push( i + '="' + CKEDITOR.tools.htmlEncode( attributes[i] ) + '" ' ); 286 html.push( '>', CKEDITOR.tools.htmlEncode( me._['default'] ), '</textarea></div>' ); 287 return html.join( '' ); 288 }; 289 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); 290 }, 291 292 /** 293 * A single checkbox with a label on the right. 294 * @constructor 295 * @extends CKEDITOR.ui.dialog.uiElement 296 * @example 297 * @param {CKEDITOR.dialog} dialog 298 * Parent dialog object. 299 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 300 * The element definition. Accepted fields: 301 * <ul> 302 * <li><strong>checked</strong> (Optional) Whether the checkbox is checked 303 * on instantiation. Defaults to false.</li> 304 * <li><strong>validate</strong> (Optional) The validation function.</li> 305 * <li><strong>label</strong> (Optional) The checkbox label.</li> 306 * </ul> 307 * @param {Array} htmlList 308 * List of HTML code to output to. 309 */ 310 checkbox : function( dialog, elementDefinition, htmlList ) 311 { 312 if ( arguments.length < 3 ) 313 return; 314 315 var _ = initPrivateObject.call( this, elementDefinition, { 'default' : !!elementDefinition[ 'default' ] } ); 316 317 if ( elementDefinition.validate ) 318 this.validate = elementDefinition.validate; 319 320 /** @ignore */ 321 var innerHTML = function() 322 { 323 var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition, 324 { 325 id : elementDefinition.id ? elementDefinition.id + '_checkbox' : CKEDITOR.tools.getNextNumber() + '_checkbox' 326 }, true ), 327 html = [], 328 attributes = { 'class' : 'cke_dialog_ui_checkbox_input', type : 'checkbox' }; 329 cleanInnerDefinition( myDefinition ); 330 if ( elementDefinition[ 'default' ] ) 331 attributes.checked = 'checked'; 332 _.checkbox = new CKEDITOR.ui.dialog.uiElement( dialog, myDefinition, html, 'input', null, attributes ); 333 html.push( ' ', CKEDITOR.tools.htmlEncode( elementDefinition.label ) ); 334 return html.join( '' ); 335 }; 336 337 CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, htmlList, 'label', null, null, innerHTML ); 338 }, 339 340 /** 341 * A group of radio buttons. 342 * @constructor 343 * @example 344 * @extends CKEDITOR.ui.dialog.labeledElement 345 * @param {CKEDITOR.dialog} dialog 346 * Parent dialog object. 347 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 348 * The element definition. Accepted fields: 349 * <ul> 350 * <li><strong>default</strong> (Required) The default value.</li> 351 * <li><strong>validate</strong> (Optional) The validation function.</li> 352 * <li><strong>items</strong> (Required) An array of options. Each option 353 * is a 1- or 2-item array of format [ 'Description', 'Value' ]. If 'Value' 354 * is missing, then the value would be assumed to be the same as the 355 * description.</li> 356 * </ul> 357 * @param {Array} htmlList 358 * List of HTML code to output to. 359 */ 360 radio : function( dialog, elementDefinition, htmlList ) 361 { 362 if ( arguments.length < 3) 363 return; 364 365 initPrivateObject.call( this, elementDefinition ); 366 if ( !this._['default'] ) 367 this._['default'] = this._.initValue = elementDefinition.items[0][1]; 368 if ( elementDefinition.validate ) 369 this.validate = elementDefinition.valdiate; 370 var children = [], me = this; 371 372 /** @ignore */ 373 var innerHTML = function() 374 { 375 var inputHtmlList = [], html = [], 376 commonAttributes = { 'class' : 'cke_dialog_ui_radio_item' }, 377 commonName = elementDefinition.id ? elementDefinition.id + '_radio' : CKEDITOR.tools.getNextNumber() + '_radio'; 378 for ( var i = 0 ; i < elementDefinition.items.length ; i++ ) 379 { 380 var item = elementDefinition.items[i], 381 title = item[2] !== undefined ? item[2] : item[0], 382 value = item[1] !== undefined ? item[1] : item[0], 383 inputDefinition = CKEDITOR.tools.extend( {}, elementDefinition, 384 { 385 id : CKEDITOR.tools.getNextNumber() + '_radio_input', 386 title : null, 387 type : null 388 }, true ), 389 labelDefinition = CKEDITOR.tools.extend( {}, inputDefinition, 390 { 391 id : null, 392 title : title 393 }, true ), 394 inputHtml = [], 395 inputAttributes = 396 { 397 type : 'radio', 398 'class' : 'cke_dialog_ui_radio_input', 399 name : commonName, 400 value : value 401 }; 402 if ( me._['default'] == value ) 403 inputAttributes.checked = 'checked'; 404 cleanInnerDefinition( inputDefinition ); 405 cleanInnerDefinition( labelDefinition ); 406 children.push( new CKEDITOR.ui.dialog.uiElement( dialog, inputDefinition, inputHtml, 'input', null, inputAttributes ) ); 407 new CKEDITOR.ui.dialog.uiElement( dialog, labelDefinition, inputHtmlList, 'label', null, null, 408 inputHtml.join( '' ) + ' ' + item[0] ); 409 } 410 new CKEDITOR.ui.dialog.hbox( dialog, [], inputHtmlList, html ); 411 return html.join( '' ); 412 }; 413 414 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); 415 this._.children = children; 416 }, 417 418 /** 419 * A button with a label inside. 420 * @constructor 421 * @example 422 * @extends CKEDITOR.ui.dialog.uiElement 423 * @param {CKEDITOR.dialog} dialog 424 * Parent dialog object. 425 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 426 * The element definition. Accepted fields: 427 * <ul> 428 * <li><strong>label</strong> (Required) The button label.</li> 429 * <li><strong>disabled</strong> (Optional) Set to true if you want the 430 * button to appear in disabled state.</li> 431 * </ul> 432 * @param {Array} htmlList 433 * List of HTML code to output to. 434 */ 435 button : function( dialog, elementDefinition, htmlList ) 436 { 437 if ( arguments.length < 3) 438 return; 439 440 if ( typeof( elementDefinition ) == 'function' ) 441 elementDefinition = elementDefinition( dialog.getParentEditor() ); 442 initPrivateObject.call( this, elementDefinition, { disabled : elementDefinition.disabled || false } ); 443 444 /** @ignore */ 445 var innerHTML = function() 446 { 447 var styles = [], 448 align = elementDefinition.align || ( dialog.getParentEditor().lang.dir == 'ltr' ? 'left' : 'right' ); 449 450 if ( elementDefinition.style ) 451 { 452 var defStyle = CKEDITOR.tools.trim( elementDefinition.style ); 453 styles.push( defStyle ); 454 if ( defStyle.charAt( defStyle.length - 1 ) != ';' ) 455 styles.push( ';' ); 456 } 457 458 // IE6 & 7 BUG: Need to set margin as well as align. 459 if ( CKEDITOR.env.ie && CKEDITOR.env.version < 8 ) 460 { 461 styles.push( [ 462 'margin:', 463 'auto', 464 align == 'right' ? '0px' : 'auto', 465 'auto', 466 align == 'left' ? '0px' : 'auto' ].join( ' ' ), ';' ); 467 } 468 469 return [ 470 '<table align="', align, '" ', styles.length > 0 ? 'style="' + styles.join( '' ) + '">' : '>', 471 '<tbody><tr><td class="cke_dialog_ui_button_txt">', 472 CKEDITOR.tools.htmlEncode( elementDefinition.label ), 473 '</td></tr></tbody></table>' 474 ].join( '' ); 475 }; 476 477 // Add OnClick event to this input. 478 CKEDITOR.event.implementOn( this ); 479 480 // Register an event handler for processing button clicks. 481 var me = this; 482 dialog.on( 'load', function( eventInfo ) 483 { 484 var element = this.getElement(); 485 (function() 486 { 487 element.on( 'mousedown', function( evt ) 488 { 489 // If button is disabled, don't do anything. 490 if ( me._.disabled ) 491 return; 492 493 // Store the currently active button. 494 CKEDITOR.ui.dialog.button._.activeButton = [ me, me.getElement() ]; 495 } ); 496 497 element.on( 'keydown', function( evt ) 498 { 499 // Click if Enter is pressed. 500 if ( evt.data.$.keyCode == 13 ) 501 { 502 me.fire( 'click', { dialog : me.getDialog() } ); 503 evt.data.preventDefault(); 504 } 505 } ); 506 })(); 507 508 // IE BUG: Padding attributes are ignored for <td> cells. 509 if ( CKEDITOR.env.ie ) 510 element.getChild( [0, 0, 0, 0] ).$.innerHTML += ''; 511 512 if ( !eventInfo.data.buttonHandlerRegistered ) 513 { 514 CKEDITOR.document.on( 'mouseup', function( evt ) 515 { 516 var target = evt.data.getTarget(), 517 activeButton = CKEDITOR.ui.dialog.button._.activeButton; 518 519 // If there's no active button, bail out. 520 if ( !activeButton ) 521 return; 522 523 // Fire the click event - but only if the 524 // active button is the same as target. 525 if ( activeButton[1].equals( target.getAscendant( 'a' ) ) ) 526 activeButton[0].fire( 'click', { dialog : activeButton[0].getDialog() } ); 527 528 // Clear active button flag. 529 CKEDITOR.ui.dialog.button._.activeButton = null; 530 } ); 531 532 eventInfo.data.buttonHandlerRegistered = true; 533 } 534 535 this.getElement().getFirst().unselectable(); 536 }, this ); 537 538 var outerDefinition = CKEDITOR.tools.extend( {}, elementDefinition ); 539 delete outerDefinition.style; 540 541 CKEDITOR.ui.dialog.uiElement.call( this, dialog, outerDefinition, htmlList, 'a', { display : 'block', outline : 'none' }, 542 { href : 'javascript:void(0);', title : elementDefinition.label, hidefocus : 'true' }, 543 innerHTML ); 544 }, 545 546 /** 547 * A select box. 548 * @extends CKEDITOR.ui.dialog.uiElement 549 * @example 550 * @constructor 551 * @param {CKEDITOR.dialog} dialog 552 * Parent dialog object. 553 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 554 * The element definition. Accepted fields: 555 * <ul> 556 * <li><strong>default</strong> (Required) The default value.</li> 557 * <li><strong>validate</strong> (Optional) The validation function.</li> 558 * <li><strong>items</strong> (Required) An array of options. Each option 559 * is a 1- or 2-item array of format [ 'Description', 'Value' ]. If 'Value' 560 * is missing, then the value would be assumed to be the same as the 561 * description.</li> 562 * <li><strong>multiple</strong> (Optional) Set this to true if you'd like 563 * to have a multiple-choice select box.</li> 564 * <li><strong>size</strong> (Optional) The number of items to display in 565 * the select box.</li> 566 * </ul> 567 * @param {Array} htmlList 568 * List of HTML code to output to. 569 */ 570 select : function( dialog, elementDefinition, htmlList ) 571 { 572 if ( arguments.length < 3 ) 573 return; 574 575 var _ = initPrivateObject.call( this, elementDefinition ); 576 577 if ( elementDefinition.validate ) 578 this.validate = elementDefinition.validate; 579 580 /** @ignore */ 581 var innerHTML = function() 582 { 583 var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition, 584 { 585 id : elementDefinition.id ? elementDefinition.id + '_select' : CKEDITOR.tools.getNextNumber() + '_select' 586 }, true ), 587 html = [], 588 innerHTML = [], 589 attributes = { 'class' : 'cke_dialog_ui_input_select' }; 590 591 // Add multiple and size attributes from element definition. 592 if ( elementDefinition.size != undefined ) 593 attributes.size = elementDefinition.size; 594 if ( elementDefinition.multiple != undefined ) 595 attributes.multiple = elementDefinition.multiple; 596 597 cleanInnerDefinition( myDefinition ); 598 for ( var i = 0, item ; i < elementDefinition.items.length && ( item = elementDefinition.items[i] ) ; i++ ) 599 { 600 innerHTML.push( '<option value="', 601 CKEDITOR.tools.htmlEncode( item[1] !== undefined ? item[1] : item[0] ), '" /> ', 602 CKEDITOR.tools.htmlEncode( item[0] ) ); 603 } 604 605 _.select = new CKEDITOR.ui.dialog.uiElement( dialog, myDefinition, html, 'select', null, attributes, innerHTML.join( '' ) ); 606 return html.join( '' ); 607 }; 608 609 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); 610 }, 611 612 /** 613 * A file upload input. 614 * @extends CKEDITOR.ui.dialog.labeledElement 615 * @example 616 * @constructor 617 * @param {CKEDITOR.dialog} dialog 618 * Parent dialog object. 619 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 620 * The element definition. Accepted fields: 621 * <ul> 622 * <li><strong>validate</strong> (Optional) The validation function.</li> 623 * </ul> 624 * @param {Array} htmlList 625 * List of HTML code to output to. 626 */ 627 file : function( dialog, elementDefinition, htmlList ) 628 { 629 if ( arguments.length < 3 ) 630 return; 631 632 if ( elementDefinition['default'] === undefined ) 633 elementDefinition['default'] = ''; 634 635 var _ = CKEDITOR.tools.extend( initPrivateObject.call( this, elementDefinition ), { definition : elementDefinition, buttons : [] } ); 636 637 if ( elementDefinition.validate ) 638 this.validate = elementDefinition.validate; 639 640 /** @ignore */ 641 var innerHTML = function() 642 { 643 _.frameId = CKEDITOR.tools.getNextNumber() + '_fileInput'; 644 var html = [ '<iframe frameborder="0" allowtransparency="0" class="cke_dialog_ui_input_file" id="', 645 _.frameId, '" src="javascript: void(0)" ></iframe>' ]; 646 return html.join( '' ); 647 }; 648 649 // IE BUG: Parent container does not resize to contain the iframe automatically. 650 dialog.on( 'load', function() 651 { 652 var iframe = CKEDITOR.document.getById( _.frameId ), 653 contentDiv = iframe.getParent(); 654 contentDiv.addClass( 'cke_dialog_ui_input_file' ); 655 } ); 656 657 CKEDITOR.ui.dialog.labeledElement.call( this, dialog, elementDefinition, htmlList, innerHTML ); 658 }, 659 660 /** 661 * A button for submitting the file in a file upload input. 662 * @extends CKEDITOR.ui.dialog.button 663 * @example 664 * @constructor 665 * @param {CKEDITOR.dialog} dialog 666 * Parent dialog object. 667 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 668 * The element definition. Accepted fields: 669 * <ul> 670 * <li><strong>for</strong> (Required) The file input's page and element Id 671 * to associate to, in a 2-item array format: [ 'page_id', 'element_id' ]. 672 * </li> 673 * <li><strong>validate</strong> (Optional) The validation function.</li> 674 * </ul> 675 * @param {Array} htmlList 676 * List of HTML code to output to. 677 */ 678 fileButton : function( dialog, elementDefinition, htmlList ) 679 { 680 if ( arguments.length < 3 ) 681 return; 682 683 var _ = initPrivateObject.call( this, elementDefinition ), 684 me = this; 685 686 if ( elementDefinition.validate ) 687 this.validate = elementDefinition.validate; 688 689 var myDefinition = CKEDITOR.tools.extend( {}, elementDefinition ); 690 myDefinition.className = ( myDefinition.className ? myDefinition.className + ' ' : '' ) + 'cke_dialog_ui_button'; 691 myDefinition.onClick = function( evt ) 692 { 693 var target = elementDefinition[ 'for' ]; // [ pageId, elementId ] 694 dialog.getContentElement( target[0], target[1] ).submit(); 695 this.disable(); 696 }; 697 698 dialog.on( 'load', function() 699 { 700 dialog.getContentElement( elementDefinition[ 'for' ][0], elementDefinition[ 'for' ][1] )._.buttons.push( me ); 701 } ); 702 703 CKEDITOR.ui.dialog.button.call( this, dialog, myDefinition, htmlList ); 704 }, 705 706 html : (function() 707 { 708 var myHtmlRe = /^\s*<[\w:]+\s+([^>]*)?>/, 709 theirHtmlRe = /^(\s*<[\w:]+(?:\s+[^>]*)?)((?:.|\r|\n)+)$/, 710 emptyTagRe = /\/$/; 711 /** 712 * A dialog element made from raw HTML code. 713 * @extends CKEDITOR.ui.dialog.uiElement 714 * @name CKEDITOR.ui.dialog.html 715 * @param {CKEDITOR.dialog} dialog Parent dialog object. 716 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition Element definition. 717 * Accepted fields: 718 * <ul> 719 * <li><strong>html</strong> (Required) HTML code of this element.</li> 720 * </ul> 721 * @param {Array} htmlList List of HTML code to be added to the dialog's content area. 722 * @example 723 * @constructor 724 */ 725 return function( dialog, elementDefinition, htmlList ) 726 { 727 if ( arguments.length < 3 ) 728 return; 729 730 var myHtmlList = [], 731 myHtml, 732 theirHtml = elementDefinition.html, 733 myMatch, theirMatch; 734 735 // If the HTML input doesn't contain any tags at the beginning, add a <span> tag around it. 736 if ( theirHtml.charAt( 0 ) != '<' ) 737 theirHtml = '<span>' + theirHtml + '</span>'; 738 739 CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition, myHtmlList, 'span', null, null, '' ); 740 741 // Append the attributes created by the uiElement call to the real HTML. 742 myHtml = myHtmlList.join( '' ); 743 myMatch = myHtml.match( myHtmlRe ); 744 theirMatch = theirHtml.match( theirHtmlRe ) || [ '', '', '' ]; 745 746 if ( emptyTagRe.test( theirMatch[1] ) ) 747 { 748 theirMatch[1] = theirMatch[1].slice( 0, -1 ); 749 theirMatch[2] = '/' + theirMatch[2]; 750 } 751 752 htmlList.push( [ theirMatch[1], ' ', myMatch[1] || '', theirMatch[2] ].join( '' ) ); 753 }; 754 })() 755 }, true ); 756 757 CKEDITOR.ui.dialog.html.prototype = new CKEDITOR.ui.dialog.uiElement; 758 759 CKEDITOR.ui.dialog.labeledElement.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, 760 /** @lends CKEDITOR.ui.dialog.labeledElement.prototype */ 761 { 762 /** 763 * Sets the label text of the element. 764 * @param {String} label The new label text. 765 * @returns {CKEDITOR.ui.dialog.labeledElement} The current labeled element. 766 * @example 767 */ 768 setLabel : function( label ) 769 { 770 var node = CKEDITOR.document.getById( this._.labelId ); 771 if ( node.getChildCount() < 1 ) 772 ( new CKEDITOR.dom.text( label, CKEDITOR.document ) ).appendTo( node ); 773 else 774 node.getChild( 0 ).$.nodeValue = label; 775 return this; 776 }, 777 778 /** 779 * Retrieves the current label text of the elment. 780 * @returns {String} The current label text. 781 * @example 782 */ 783 getLabel : function() 784 { 785 var node = CKEDITOR.document.getById( this._.labelId ); 786 if ( !node || node.getChildCount() < 1 ) 787 return ''; 788 else 789 return node.getChild( 0 ).getText(); 790 }, 791 792 /** 793 * Defines the onChange event for UI element definitions. 794 * @field 795 * @type Object 796 * @example 797 */ 798 eventProcessors : commonEventProcessors 799 }, true ); 800 801 CKEDITOR.ui.dialog.button.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, 802 /** @lends CKEDITOR.ui.dialog.button.prototype */ 803 { 804 /** 805 * Simulates a click to the button. 806 * @example 807 * @returns {Object} Return value of the 'click' event. 808 */ 809 click : function() 810 { 811 if ( !this._.disabled ) 812 return this.fire( 'click', { dialog : this._.dialog } ); 813 this.getElement().$.blur(); 814 }, 815 816 /** 817 * Enables the button. 818 * @example 819 */ 820 enable : function() 821 { 822 this._.disabled = false; 823 this.getElement().removeClass( 'disabled' ); 824 }, 825 826 /** 827 * Disables the button. 828 * @example 829 */ 830 disable : function() 831 { 832 this._.disabled = true; 833 this.getElement().addClass( 'disabled' ); 834 }, 835 836 isVisible : function() 837 { 838 return !!this.getElement().$.firstChild.offsetHeight; 839 }, 840 841 isEnabled : function() 842 { 843 return !this._.disabled; 844 }, 845 846 /** 847 * Defines the onChange event and onClick for button element definitions. 848 * @field 849 * @type Object 850 * @example 851 */ 852 eventProcessors : CKEDITOR.tools.extend( {}, CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors, 853 { 854 /** @ignore */ 855 onClick : function( dialog, func ) 856 { 857 this.on( 'click', func ); 858 } 859 }, true ), 860 861 /** 862 * Handler for the element's access key up event. Simulates a click to 863 * the button. 864 * @example 865 */ 866 accessKeyUp : function() 867 { 868 this.click(); 869 }, 870 871 /** 872 * Handler for the element's access key down event. Simulates a mouse 873 * down to the button. 874 * @example 875 */ 876 accessKeyDown : function() 877 { 878 this.focus(); 879 }, 880 881 keyboardFocusable : true 882 }, true ); 883 884 CKEDITOR.ui.dialog.textInput.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement, 885 /** @lends CKEDITOR.ui.dialog.textInput.prototype */ 886 { 887 /** 888 * Gets the text input DOM element under this UI object. 889 * @example 890 * @returns {CKEDITOR.dom.element} The DOM element of the text input. 891 */ 892 getInputElement : function() 893 { 894 return CKEDITOR.document.getById( this._.inputId ); 895 }, 896 897 /** 898 * Puts focus into the text input. 899 * @example 900 */ 901 focus : function() 902 { 903 var me = this.selectParentTab(); 904 905 // GECKO BUG: setTimeout() is needed to workaround invisible selections. 906 setTimeout( function(){ me.getInputElement().$.focus(); }, 0 ); 907 }, 908 909 /** 910 * Selects all the text in the text input. 911 * @example 912 */ 913 select : function() 914 { 915 var me = this.selectParentTab(); 916 917 // GECKO BUG: setTimeout() is needed to workaround invisible selections. 918 setTimeout( function(){ var e = me.getInputElement().$; e.focus(); e.select(); }, 0 ); 919 }, 920 921 /** 922 * Handler for the text input's access key up event. Makes a select() 923 * call to the text input. 924 * @example 925 */ 926 accessKeyUp : function() 927 { 928 this.select(); 929 }, 930 931 /** 932 * Sets the value of this text input object. 933 * @param {Object} value The new value. 934 * @returns {CKEDITOR.ui.dialog.textInput} The current UI element. 935 * @example 936 * uiElement.setValue( 'Blamo' ); 937 */ 938 setValue : function( value ) 939 { 940 value = value || ''; 941 return CKEDITOR.ui.dialog.uiElement.prototype.setValue.call( this, value ); 942 }, 943 944 keyboardFocusable : true 945 }, commonPrototype, true ); 946 947 CKEDITOR.ui.dialog.textarea.prototype = new CKEDITOR.ui.dialog.textInput(); 948 949 CKEDITOR.ui.dialog.select.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement, 950 /** @lends CKEDITOR.ui.dialog.select.prototype */ 951 { 952 /** 953 * Gets the DOM element of the select box. 954 * @returns {CKEDITOR.dom.element} The <select> element of this UI 955 * element. 956 * @example 957 */ 958 getInputElement : function() 959 { 960 return this._.select.getElement(); 961 }, 962 963 /** 964 * Adds an option to the select box. 965 * @param {String} label Option label. 966 * @param {String} value (Optional) Option value, if not defined it'll be 967 * assumed to be the same as the label. 968 * @param {Number} index (Optional) Position of the option to be inserted 969 * to. If not defined the new option will be inserted to the end of list. 970 * @example 971 * @returns {CKEDITOR.ui.dialog.select} The current select UI element. 972 */ 973 add : function( label, value, index ) 974 { 975 var option = new CKEDITOR.dom.element( 'option', this.getDialog().getParentEditor().document ), 976 selectElement = this.getInputElement().$; 977 option.$.text = label; 978 option.$.value = ( value === undefined || value === null ) ? label : value; 979 if ( index === undefined || index === null ) 980 { 981 if ( CKEDITOR.env.ie ) 982 selectElement.add( option.$ ); 983 else 984 selectElement.add( option.$, null ); 985 } 986 else 987 selectElement.add( option.$, index ); 988 return this; 989 }, 990 991 /** 992 * Removes an option from the selection list. 993 * @param {Number} index Index of the option to be removed. 994 * @example 995 * @returns {CKEDITOR.ui.dialog.select} The current select UI element. 996 */ 997 remove : function( index ) 998 { 999 var selectElement = this.getInputElement().$; 1000 selectElement.remove( index ); 1001 return this; 1002 }, 1003 1004 /** 1005 * Clears all options out of the selection list. 1006 * @returns {CKEDITOR.ui.dialog.select} The current select UI element. 1007 */ 1008 clear : function() 1009 { 1010 var selectElement = this.getInputElement().$; 1011 while ( selectElement.length > 0 ) 1012 selectElement.remove( 0 ); 1013 return this; 1014 }, 1015 1016 keyboardFocusable : true 1017 }, commonPrototype, true ); 1018 1019 CKEDITOR.ui.dialog.checkbox.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, 1020 /** @lends CKEDITOR.ui.dialog.checkbox.prototype */ 1021 { 1022 /** 1023 * Gets the checkbox DOM element. 1024 * @example 1025 * @returns {CKEDITOR.dom.element} The DOM element of the checkbox. 1026 */ 1027 getInputElement : function() 1028 { 1029 return this._.checkbox.getElement(); 1030 }, 1031 1032 /** 1033 * Sets the state of the checkbox. 1034 * @example 1035 * @param {Boolean} true to tick the checkbox, false to untick it. 1036 */ 1037 setValue : function( checked ) 1038 { 1039 this.getInputElement().$.checked = checked; 1040 this.fire( 'change', { value : checked } ); 1041 }, 1042 1043 /** 1044 * Gets the state of the checkbox. 1045 * @example 1046 * @returns {Boolean} true means the checkbox is ticked, false means it's not ticked. 1047 */ 1048 getValue : function() 1049 { 1050 return this.getInputElement().$.checked; 1051 }, 1052 1053 /** 1054 * Handler for the access key up event. Toggles the checkbox. 1055 * @example 1056 */ 1057 accessKeyUp : function() 1058 { 1059 this.setValue( !this.getValue() ); 1060 }, 1061 1062 /** 1063 * Defines the onChange event for UI element definitions. 1064 * @field 1065 * @type Object 1066 * @example 1067 */ 1068 eventProcessors : 1069 { 1070 onChange : function( dialog, func ) 1071 { 1072 if ( !CKEDITOR.env.ie ) 1073 return commonEventProcessors.onChange.apply( this, arguments ); 1074 else 1075 { 1076 dialog.on( 'load', function() 1077 { 1078 var element = this._.checkbox.getElement(); 1079 element.on( 'propertychange', function( evt ) 1080 { 1081 evt = evt.data.$; 1082 if ( evt.propertyName == 'checked' ) 1083 this.fire( 'change', { value : element.$.checked } ); 1084 }, this ); 1085 }, this ); 1086 this.on( 'change', func ); 1087 } 1088 return null; 1089 } 1090 }, 1091 1092 keyboardFocusable : true 1093 }, commonPrototype, true ); 1094 1095 CKEDITOR.ui.dialog.radio.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, 1096 /** @lends CKEDITOR.ui.dialog.radio.prototype */ 1097 { 1098 /** 1099 * Checks one of the radio buttons in this button group. 1100 * @example 1101 * @param {String} value The value of the button to be chcked. 1102 */ 1103 setValue : function( value ) 1104 { 1105 var children = this._.children, 1106 item; 1107 for ( var i = 0 ; ( i < children.length ) && ( item = children[i] ) ; i++ ) 1108 item.getElement().$.checked = ( item.getValue() == value ); 1109 this.fire( 'change', { value : value } ); 1110 }, 1111 1112 /** 1113 * Gets the value of the currently checked radio button. 1114 * @example 1115 * @returns {String} The currently checked button's value. 1116 */ 1117 getValue : function() 1118 { 1119 var children = this._.children; 1120 for ( var i = 0 ; i < children.length ; i++ ) 1121 { 1122 if ( children[i].getElement().$.checked ) 1123 return children[i].getValue(); 1124 } 1125 return null; 1126 }, 1127 1128 /** 1129 * Handler for the access key up event. Focuses the currently 1130 * selected radio button, or the first radio button if none is 1131 * selected. 1132 * @example 1133 */ 1134 accessKeyUp : function() 1135 { 1136 var children = this._.children, i; 1137 for ( i = 0 ; i < children.length ; i++ ) 1138 { 1139 if ( children[i].getElement().$.checked ) 1140 { 1141 children[i].getElement().focus(); 1142 return; 1143 } 1144 } 1145 children[0].getElement().focus(); 1146 }, 1147 1148 /** 1149 * Defines the onChange event for UI element definitions. 1150 * @field 1151 * @type Object 1152 * @example 1153 */ 1154 eventProcessors : 1155 { 1156 onChange : function( dialog, func ) 1157 { 1158 if ( !CKEDITOR.env.ie ) 1159 return commonEventProcessors.onChange.apply( this, arguments ); 1160 else 1161 { 1162 dialog.on( 'load', function() 1163 { 1164 var children = this._.children, me = this; 1165 for ( var i = 0 ; i < children.length ; i++ ) 1166 { 1167 var element = children[i].getElement(); 1168 element.on( 'propertychange', function( evt ) 1169 { 1170 evt = evt.data.$; 1171 if ( evt.propertyName == 'checked' && this.$.checked ) 1172 me.fire( 'change', { value : this.getAttribute( 'value' ) } ); 1173 } ); 1174 } 1175 }, this ); 1176 this.on( 'change', func ); 1177 } 1178 return null; 1179 } 1180 }, 1181 1182 keyboardFocusable : true 1183 }, commonPrototype, true ); 1184 1185 CKEDITOR.ui.dialog.file.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.labeledElement, 1186 commonPrototype, 1187 /** @lends CKEDITOR.ui.dialog.file.prototype */ 1188 { 1189 /** 1190 * Gets the <input> element of this file input. 1191 * @returns {CKEDITOR.dom.element} The file input element. 1192 * @example 1193 */ 1194 getInputElement : function() 1195 { 1196 return new CKEDITOR.dom.element( CKEDITOR.document.getById( this._.frameId ) 1197 .$.contentWindow.document.forms[0].elements[0] ); 1198 }, 1199 1200 /** 1201 * Uploads the file in the file input. 1202 * @returns {CKEDITOR.ui.dialog.file} This object. 1203 * @example 1204 */ 1205 submit : function() 1206 { 1207 this.getInputElement().getParent().$.submit(); 1208 return this; 1209 }, 1210 1211 /** 1212 * Redraws the file input and resets the file path in the file input. 1213 * The redraw logic is necessary because non-IE browsers tend to clear 1214 * the <iframe> containing the file input after closing the dialog. 1215 * @example 1216 */ 1217 reset : function() 1218 { 1219 var frameElement = CKEDITOR.document.getById( this._.frameId ), 1220 frameDocument = frameElement.$.contentWindow.document, 1221 elementDefinition = this._.definition, 1222 buttons = this._.buttons; 1223 frameDocument.open(); 1224 frameDocument.write( [ '<html><head><title></title></head><body style="margin: 0; overflow: hidden; background: transparent;">', 1225 '<form enctype="multipart/form-data" method="POST" action="', 1226 CKEDITOR.tools.htmlEncode( elementDefinition.action ), 1227 '">', 1228 '<input type="file" name="', 1229 CKEDITOR.tools.htmlEncode( elementDefinition.id || 'cke_upload' ), 1230 '" size="', 1231 CKEDITOR.tools.htmlEncode( elementDefinition.size || '' ), 1232 '" />', 1233 '</form>', 1234 '</body></html>' ].join( '' ) ); 1235 frameDocument.close(); 1236 1237 for ( var i = 0 ; i < buttons.length ; i++ ) 1238 buttons[i].enable(); 1239 }, 1240 1241 /** 1242 * Defines the onChange event for UI element definitions. 1243 * @field 1244 * @type Object 1245 * @example 1246 */ 1247 eventProcessors : commonEventProcessors, 1248 1249 keyboardFocusable : true 1250 }, true ); 1251 1252 CKEDITOR.ui.dialog.fileButton.prototype = new CKEDITOR.ui.dialog.button; 1253 1254 CKEDITOR.ui.dialog.button._ = { activeButton : null }; 1255 1256 CKEDITOR.dialog.addUIElement( 'text', textBuilder ); 1257 CKEDITOR.dialog.addUIElement( 'password', textBuilder ); 1258 CKEDITOR.dialog.addUIElement( 'textarea', commonBuilder ); 1259 CKEDITOR.dialog.addUIElement( 'checkbox', commonBuilder ); 1260 CKEDITOR.dialog.addUIElement( 'radio', commonBuilder ); 1261 CKEDITOR.dialog.addUIElement( 'button', commonBuilder ); 1262 CKEDITOR.dialog.addUIElement( 'select', commonBuilder ); 1263 CKEDITOR.dialog.addUIElement( 'file', commonBuilder ); 1264 CKEDITOR.dialog.addUIElement( 'fileButton', commonBuilder ); 1265 CKEDITOR.dialog.addUIElement( 'html', commonBuilder ); 1266 })(); 1267