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