| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 /* 2 Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 /** 7 * @fileOverview The floating dialog plugin. 8 */ 9 10 /** 11 * No resize for this dialog. 12 * @constant 13 */ 14 CKEDITOR.DIALOG_RESIZE_NONE = 0; 15 16 /** 17 * Only allow horizontal resizing for this dialog, disable vertical resizing. 18 * @constant 19 */ 20 CKEDITOR.DIALOG_RESIZE_WIDTH = 1; 21 22 /** 23 * Only allow vertical resizing for this dialog, disable horizontal resizing. 24 * @constant 25 */ 26 CKEDITOR.DIALOG_RESIZE_HEIGHT = 2; 27 28 /* 29 * Allow the dialog to be resized in both directions. 30 * @constant 31 */ 32 CKEDITOR.DIALOG_RESIZE_BOTH = 3; 33 34 (function() 35 { 36 function isTabVisible( tabId ) 37 { 38 return !!this._.tabs[ tabId ][ 0 ].$.offsetHeight; 39 } 40 41 function getPreviousVisibleTab() 42 { 43 var tabId = this._.currentTabId, 44 length = this._.tabIdList.length, 45 tabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, tabId ) + length; 46 47 for ( var i = tabIndex - 1 ; i > tabIndex - length ; i-- ) 48 { 49 if ( isTabVisible.call( this, this._.tabIdList[ i % length ] ) ) 50 return this._.tabIdList[ i % length ]; 51 } 52 53 return null; 54 } 55 56 function getNextVisibleTab() 57 { 58 var tabId = this._.currentTabId, 59 length = this._.tabIdList.length, 60 tabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, tabId ); 61 62 for ( var i = tabIndex + 1 ; i < tabIndex + length ; i++ ) 63 { 64 if ( isTabVisible.call( this, this._.tabIdList[ i % length ] ) ) 65 return this._.tabIdList[ i % length ]; 66 } 67 68 return null; 69 } 70 71 /** 72 * This is the base class for runtime dialog objects. An instance of this 73 * class represents a single named dialog for a single editor instance. 74 * @param {Object} editor The editor which created the dialog. 75 * @param {String} dialogName The dialog's registered name. 76 * @constructor 77 * @example 78 * var dialogObj = new CKEDITOR.dialog( editor, 'smiley' ); 79 */ 80 CKEDITOR.dialog = function( editor, dialogName ) 81 { 82 // Load the dialog definition. 83 var definition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ]; 84 85 // Completes the definition with the default values. 86 definition = CKEDITOR.tools.extend( definition( editor ), defaultDialogDefinition ); 87 88 // Clone a functionally independent copy for this dialog. 89 definition = CKEDITOR.tools.clone( definition ); 90 91 // Create a complex definition object, extending it with the API 92 // functions. 93 definition = new definitionObject( this, definition ); 94 95 96 var doc = CKEDITOR.document; 97 98 var themeBuilt = editor.theme.buildDialog( editor ); 99 100 // Initialize some basic parameters. 101 this._ = 102 { 103 editor : editor, 104 element : themeBuilt.element, 105 name : dialogName, 106 contentSize : { width : 0, height : 0 }, 107 size : { width : 0, height : 0 }, 108 updateSize : false, 109 contents : {}, 110 buttons : {}, 111 accessKeyMap : {}, 112 113 // Initialize the tab and page map. 114 tabs : {}, 115 tabIdList : [], 116 currentTabId : null, 117 currentTabIndex : null, 118 pageCount : 0, 119 lastTab : null, 120 tabBarMode : false, 121 122 // Initialize the tab order array for input widgets. 123 focusList : [], 124 currentFocusIndex : 0, 125 hasFocus : false 126 }; 127 128 this.parts = themeBuilt.parts; 129 130 CKEDITOR.tools.setTimeout( function() 131 { 132 editor.fire( 'ariaWidget', this.parts.contents ); 133 }, 134 0, this ); 135 136 // Set the startup styles for the dialog, avoiding it enlarging the 137 // page size on the dialog creation. 138 this.parts.dialog.setStyles( 139 { 140 position : CKEDITOR.env.ie6Compat ? 'absolute' : 'fixed', 141 top : 0, 142 left: 0, 143 visibility : 'hidden' 144 }); 145 146 // Call the CKEDITOR.event constructor to initialize this instance. 147 CKEDITOR.event.call( this ); 148 149 // Fire the "dialogDefinition" event, making it possible to customize 150 // the dialog definition. 151 this.definition = definition = CKEDITOR.fire( 'dialogDefinition', 152 { 153 name : dialogName, 154 definition : definition 155 } 156 , editor ).definition; 157 // Initialize load, show, hide, ok and cancel events. 158 if ( definition.onLoad ) 159 this.on( 'load', definition.onLoad ); 160 161 if ( definition.onShow ) 162 this.on( 'show', definition.onShow ); 163 164 if ( definition.onHide ) 165 this.on( 'hide', definition.onHide ); 166 167 if ( definition.onOk ) 168 { 169 this.on( 'ok', function( evt ) 170 { 171 // Dialog confirm might probably introduce content changes (#5415). 172 editor.fire( 'saveSnapshot' ); 173 setTimeout( function () { editor.fire( 'saveSnapshot' ); }, 0 ); 174 if ( definition.onOk.call( this, evt ) === false ) 175 evt.data.hide = false; 176 }); 177 } 178 179 if ( definition.onCancel ) 180 { 181 this.on( 'cancel', function( evt ) 182 { 183 if ( definition.onCancel.call( this, evt ) === false ) 184 evt.data.hide = false; 185 }); 186 } 187 188 var me = this; 189 190 // Iterates over all items inside all content in the dialog, calling a 191 // function for each of them. 192 var iterContents = function( func ) 193 { 194 var contents = me._.contents, 195 stop = false; 196 197 for ( var i in contents ) 198 { 199 for ( var j in contents[i] ) 200 { 201 stop = func.call( this, contents[i][j] ); 202 if ( stop ) 203 return; 204 } 205 } 206 }; 207 208 this.on( 'ok', function( evt ) 209 { 210 iterContents( function( item ) 211 { 212 if ( item.validate ) 213 { 214 var isValid = item.validate( this ); 215 216 if ( typeof isValid == 'string' ) 217 { 218 alert( isValid ); 219 isValid = false; 220 } 221 222 if ( isValid === false ) 223 { 224 if ( item.select ) 225 item.select(); 226 else 227 item.focus(); 228 229 evt.data.hide = false; 230 evt.stop(); 231 return true; 232 } 233 } 234 }); 235 }, this, null, 0 ); 236 237 this.on( 'cancel', function( evt ) 238 { 239 iterContents( function( item ) 240 { 241 if ( item.isChanged() ) 242 { 243 if ( !confirm( editor.lang.common.confirmCancel ) ) 244 evt.data.hide = false; 245 return true; 246 } 247 }); 248 }, this, null, 0 ); 249 250 this.parts.close.on( 'click', function( evt ) 251 { 252 if ( this.fire( 'cancel', { hide : true } ).hide !== false ) 253 this.hide(); 254 evt.data.preventDefault(); 255 }, this ); 256 257 // Sort focus list according to tab order definitions. 258 function setupFocus() 259 { 260 var focusList = me._.focusList; 261 focusList.sort( function( a, b ) 262 { 263 // Mimics browser tab order logics; 264 if ( a.tabIndex != b.tabIndex ) 265 return b.tabIndex - a.tabIndex; 266 // Sort is not stable in some browsers, 267 // fall-back the comparator to 'focusIndex'; 268 else 269 return a.focusIndex - b.focusIndex; 270 }); 271 272 var size = focusList.length; 273 for ( var i = 0; i < size; i++ ) 274 focusList[ i ].focusIndex = i; 275 } 276 277 function changeFocus( forward ) 278 { 279 var focusList = me._.focusList, 280 offset = forward ? 1 : -1; 281 if ( focusList.length < 1 ) 282 return; 283 284 var current = me._.currentFocusIndex; 285 286 // Trigger the 'blur' event of any input element before anything, 287 // since certain UI updates may depend on it. 288 try 289 { 290 focusList[ current ].getInputElement().$.blur(); 291 } 292 catch( e ){} 293 294 var startIndex = ( current + offset + focusList.length ) % focusList.length, 295 currentIndex = startIndex; 296 while ( !focusList[ currentIndex ].isFocusable() ) 297 { 298 currentIndex = ( currentIndex + offset + focusList.length ) % focusList.length; 299 if ( currentIndex == startIndex ) 300 break; 301 } 302 focusList[ currentIndex ].focus(); 303 304 // Select whole field content. 305 if ( focusList[ currentIndex ].type == 'text' ) 306 focusList[ currentIndex ].select(); 307 } 308 309 this.changeFocus = changeFocus; 310 311 var processed; 312 313 function focusKeydownHandler( evt ) 314 { 315 // If I'm not the top dialog, ignore. 316 if ( me != CKEDITOR.dialog._.currentTop ) 317 return; 318 319 var keystroke = evt.data.getKeystroke(), 320 rtl = editor.lang.dir == 'rtl'; 321 322 processed = 0; 323 if ( keystroke == 9 || keystroke == CKEDITOR.SHIFT + 9 ) 324 { 325 var shiftPressed = ( keystroke == CKEDITOR.SHIFT + 9 ); 326 327 // Handling Tab and Shift-Tab. 328 if ( me._.tabBarMode ) 329 { 330 // Change tabs. 331 var nextId = shiftPressed ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ); 332 me.selectPage( nextId ); 333 me._.tabs[ nextId ][ 0 ].focus(); 334 } 335 else 336 { 337 // Change the focus of inputs. 338 changeFocus( !shiftPressed ); 339 } 340 341 processed = 1; 342 } 343 else if ( keystroke == CKEDITOR.ALT + 121 && !me._.tabBarMode && me.getPageCount() > 1 ) 344 { 345 // Alt-F10 puts focus into the current tab item in the tab bar. 346 me._.tabBarMode = true; 347 me._.tabs[ me._.currentTabId ][ 0 ].focus(); 348 processed = 1; 349 } 350 else if ( ( keystroke == 37 || keystroke == 39 ) && me._.tabBarMode ) 351 { 352 // Arrow keys - used for changing tabs. 353 nextId = ( keystroke == ( rtl ? 39 : 37 ) ? getPreviousVisibleTab.call( me ) : getNextVisibleTab.call( me ) ); 354 me.selectPage( nextId ); 355 me._.tabs[ nextId ][ 0 ].focus(); 356 processed = 1; 357 } 358 else if ( ( keystroke == 13 || keystroke == 32 ) && me._.tabBarMode ) 359 { 360 this.selectPage( this._.currentTabId ); 361 this._.tabBarMode = false; 362 this._.currentFocusIndex = -1; 363 changeFocus( true ); 364 processed = 1; 365 } 366 367 if ( processed ) 368 { 369 evt.stop(); 370 evt.data.preventDefault(); 371 } 372 } 373 374 function focusKeyPressHandler( evt ) 375 { 376 processed && evt.data.preventDefault(); 377 } 378 379 var dialogElement = this._.element; 380 // Add the dialog keyboard handlers. 381 this.on( 'show', function() 382 { 383 dialogElement.on( 'keydown', focusKeydownHandler, this, null, 0 ); 384 // Some browsers instead, don't cancel key events in the keydown, but in the 385 // keypress. So we must do a longer trip in those cases. (#4531) 386 if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) ) 387 dialogElement.on( 'keypress', focusKeyPressHandler, this ); 388 389 } ); 390 this.on( 'hide', function() 391 { 392 dialogElement.removeListener( 'keydown', focusKeydownHandler ); 393 if ( CKEDITOR.env.opera || ( CKEDITOR.env.gecko && CKEDITOR.env.mac ) ) 394 dialogElement.removeListener( 'keypress', focusKeyPressHandler ); 395 } ); 396 this.on( 'iframeAdded', function( evt ) 397 { 398 var doc = new CKEDITOR.dom.document( evt.data.iframe.$.contentWindow.document ); 399 doc.on( 'keydown', focusKeydownHandler, this, null, 0 ); 400 } ); 401 402 // Auto-focus logic in dialog. 403 this.on( 'show', function() 404 { 405 // Setup tabIndex on showing the dialog instead of on loading 406 // to allow dynamic tab order happen in dialog definition. 407 setupFocus(); 408 409 if ( editor.config.dialog_startupFocusTab 410 && me._.pageCount > 1 ) 411 { 412 me._.tabBarMode = true; 413 me._.tabs[ me._.currentTabId ][ 0 ].focus(); 414 } 415 else if ( !this._.hasFocus ) 416 { 417 this._.currentFocusIndex = -1; 418 419 // Decide where to put the initial focus. 420 if ( definition.onFocus ) 421 { 422 var initialFocus = definition.onFocus.call( this ); 423 // Focus the field that the user specified. 424 initialFocus && initialFocus.focus(); 425 } 426 // Focus the first field in layout order. 427 else 428 changeFocus( true ); 429 430 /* 431 * IE BUG: If the initial focus went into a non-text element (e.g. button), 432 * then IE would still leave the caret inside the editing area. 433 */ 434 if ( this._.editor.mode == 'wysiwyg' && CKEDITOR.env.ie ) 435 { 436 var $selection = editor.document.$.selection, 437 $range = $selection.createRange(); 438 439 if ( $range ) 440 { 441 if ( $range.parentElement && $range.parentElement().ownerDocument == editor.document.$ 442 || $range.item && $range.item( 0 ).ownerDocument == editor.document.$ ) 443 { 444 var $myRange = document.body.createTextRange(); 445 $myRange.moveToElementText( this.getElement().getFirst().$ ); 446 $myRange.collapse( true ); 447 $myRange.select(); 448 } 449 } 450 } 451 } 452 }, this, null, 0xffffffff ); 453 454 // IE6 BUG: Text fields and text areas are only half-rendered the first time the dialog appears in IE6 (#2661). 455 // This is still needed after [2708] and [2709] because text fields in hidden TR tags are still broken. 456 if ( CKEDITOR.env.ie6Compat ) 457 { 458 this.on( 'load', function( evt ) 459 { 460 var outer = this.getElement(), 461 inner = outer.getFirst(); 462 inner.remove(); 463 inner.appendTo( outer ); 464 }, this ); 465 } 466 467 initDragAndDrop( this ); 468 initResizeHandles( this ); 469 470 // Insert the title. 471 ( new CKEDITOR.dom.text( definition.title, CKEDITOR.document ) ).appendTo( this.parts.title ); 472 473 // Insert the tabs and contents. 474 for ( var i = 0 ; i < definition.contents.length ; i++ ) 475 { 476 var page = definition.contents[i]; 477 page && this.addPage( page ); 478 } 479 480 this.parts['tabs'].on( 'click', function( evt ) 481 { 482 var target = evt.data.getTarget(); 483 // If we aren't inside a tab, bail out. 484 if ( target.hasClass( 'cke_dialog_tab' ) ) 485 { 486 // Get the ID of the tab, without the 'cke_' prefix and the unique number suffix. 487 var id = target.$.id; 488 this.selectPage( id.substring( 4, id.lastIndexOf( '_' ) ) ); 489 490 if ( this._.tabBarMode ) 491 { 492 this._.tabBarMode = false; 493 this._.currentFocusIndex = -1; 494 changeFocus( true ); 495 } 496 evt.data.preventDefault(); 497 } 498 }, this ); 499 500 // Insert buttons. 501 var buttonsHtml = [], 502 buttons = CKEDITOR.dialog._.uiElementBuilders.hbox.build( this, 503 { 504 type : 'hbox', 505 className : 'cke_dialog_footer_buttons', 506 widths : [], 507 children : definition.buttons 508 }, buttonsHtml ).getChild(); 509 this.parts.footer.setHtml( buttonsHtml.join( '' ) ); 510 511 for ( i = 0 ; i < buttons.length ; i++ ) 512 this._.buttons[ buttons[i].id ] = buttons[i]; 513 }; 514 515 // Focusable interface. Use it via dialog.addFocusable. 516 function Focusable( dialog, element, index ) 517 { 518 this.element = element; 519 this.focusIndex = index; 520 // TODO: support tabIndex for focusables. 521 this.tabIndex = 0; 522 this.isFocusable = function() 523 { 524 return !element.getAttribute( 'disabled' ) && element.isVisible(); 525 }; 526 this.focus = function() 527 { 528 dialog._.currentFocusIndex = this.focusIndex; 529 this.element.focus(); 530 }; 531 // Bind events 532 element.on( 'keydown', function( e ) 533 { 534 if ( e.data.getKeystroke() in { 32:1, 13:1 } ) 535 this.fire( 'click' ); 536 } ); 537 element.on( 'focus', function() 538 { 539 this.fire( 'mouseover' ); 540 } ); 541 element.on( 'blur', function() 542 { 543 this.fire( 'mouseout' ); 544 } ); 545 } 546 547 CKEDITOR.dialog.prototype = 548 { 549 destroy : function() 550 { 551 this.hide(); 552 this._.element.remove(); 553 }, 554 555 /** 556 * Resizes the dialog. 557 * @param {Number} width The width of the dialog in pixels. 558 * @param {Number} height The height of the dialog in pixels. 559 * @function 560 * @example 561 * dialogObj.resize( 800, 640 ); 562 */ 563 resize : (function() 564 { 565 return function( width, height ) 566 { 567 if ( this._.contentSize && this._.contentSize.width == width && this._.contentSize.height == height ) 568 return; 569 570 CKEDITOR.dialog.fire( 'resize', 571 { 572 dialog : this, 573 skin : this._.editor.skinName, 574 width : width, 575 height : height 576 }, this._.editor ); 577 578 this._.contentSize = { width : width, height : height }; 579 this._.updateSize = true; 580 }; 581 })(), 582 583 /** 584 * Gets the current size of the dialog in pixels. 585 * @returns {Object} An object with "width" and "height" properties. 586 * @example 587 * var width = dialogObj.getSize().width; 588 */ 589 getSize : function() 590 { 591 if ( !this._.updateSize ) 592 return this._.size; 593 var element = this._.element.getFirst(); 594 var size = this._.size = { width : element.$.offsetWidth || 0, height : element.$.offsetHeight || 0}; 595 596 // If either the offsetWidth or offsetHeight is 0, the element isn't visible. 597 this._.updateSize = !size.width || !size.height; 598 599 return size; 600 }, 601 602 /** 603 * Moves the dialog to an (x, y) coordinate relative to the window. 604 * @function 605 * @param {Number} x The target x-coordinate. 606 * @param {Number} y The target y-coordinate. 607 * @example 608 * dialogObj.move( 10, 40 ); 609 */ 610 move : (function() 611 { 612 var isFixed; 613 return function( x, y ) 614 { 615 // The dialog may be fixed positioned or absolute positioned. Ask the 616 // browser what is the current situation first. 617 var element = this._.element.getFirst(); 618 if ( isFixed === undefined ) 619 isFixed = element.getComputedStyle( 'position' ) == 'fixed'; 620 621 if ( isFixed && this._.position && this._.position.x == x && this._.position.y == y ) 622 return; 623 624 // Save the current position. 625 this._.position = { x : x, y : y }; 626 627 // If not fixed positioned, add scroll position to the coordinates. 628 if ( !isFixed ) 629 { 630 var scrollPosition = CKEDITOR.document.getWindow().getScrollPosition(); 631 x += scrollPosition.x; 632 y += scrollPosition.y; 633 } 634 635 element.setStyles( 636 { 637 'left' : ( x > 0 ? x : 0 ) + 'px', 638 'top' : ( y > 0 ? y : 0 ) + 'px' 639 }); 640 }; 641 })(), 642 643 /** 644 * Gets the dialog's position in the window. 645 * @returns {Object} An object with "x" and "y" properties. 646 * @example 647 * var dialogX = dialogObj.getPosition().x; 648 */ 649 getPosition : function(){ return CKEDITOR.tools.extend( {}, this._.position ); }, 650 651 /** 652 * Shows the dialog box. 653 * @example 654 * dialogObj.show(); 655 */ 656 show : function() 657 { 658 var editor = this._.editor; 659 if ( editor.mode == 'wysiwyg' && CKEDITOR.env.ie ) 660 { 661 var selection = editor.getSelection(); 662 selection && selection.lock(); 663 } 664 665 // Insert the dialog's element to the root document. 666 var element = this._.element; 667 var definition = this.definition; 668 if ( !( element.getParent() && element.getParent().equals( CKEDITOR.document.getBody() ) ) ) 669 element.appendTo( CKEDITOR.document.getBody() ); 670 else 671 element.setStyle( 'display', 'block' ); 672 673 // FIREFOX BUG: Fix vanishing caret for Firefox 2 or Gecko 1.8. 674 if ( CKEDITOR.env.gecko && CKEDITOR.env.version < 10900 ) 675 { 676 var dialogElement = this.parts.dialog; 677 dialogElement.setStyle( 'position', 'absolute' ); 678 setTimeout( function() 679 { 680 dialogElement.setStyle( 'position', 'fixed' ); 681 }, 0 ); 682 } 683 684 685 // First, set the dialog to an appropriate size. 686 this.resize( definition.minWidth, definition.minHeight ); 687 688 // Select the first tab by default. 689 this.selectPage( this.definition.contents[0].id ); 690 691 // Reset all inputs back to their default value. 692 this.reset(); 693 694 // Set z-index. 695 if ( CKEDITOR.dialog._.currentZIndex === null ) 696 CKEDITOR.dialog._.currentZIndex = this._.editor.config.baseFloatZIndex; 697 this._.element.getFirst().setStyle( 'z-index', CKEDITOR.dialog._.currentZIndex += 10 ); 698 699 // Maintain the dialog ordering and dialog cover. 700 // Also register key handlers if first dialog. 701 if ( CKEDITOR.dialog._.currentTop === null ) 702 { 703 CKEDITOR.dialog._.currentTop = this; 704 this._.parentDialog = null; 705 showCover( this._.editor ); 706 707 element.on( 'keydown', accessKeyDownHandler ); 708 element.on( CKEDITOR.env.opera ? 'keypress' : 'keyup', accessKeyUpHandler ); 709 710 // Prevent some keys from bubbling up. (#4269) 711 for ( var event in { keyup :1, keydown :1, keypress :1 } ) 712 element.on( event, preventKeyBubbling ); 713 } 714 else 715 { 716 this._.parentDialog = CKEDITOR.dialog._.currentTop; 717 var parentElement = this._.parentDialog.getElement().getFirst(); 718 parentElement.$.style.zIndex -= Math.floor( this._.editor.config.baseFloatZIndex / 2 ); 719 CKEDITOR.dialog._.currentTop = this; 720 } 721 722 // Register the Esc hotkeys. 723 registerAccessKey( this, this, '\x1b', null, function() 724 { 725 this.getButton( 'cancel' ) && this.getButton( 'cancel' ).click(); 726 } ); 727 728 // Reset the hasFocus state. 729 this._.hasFocus = false; 730 731 // Rearrange the dialog to the middle of the window. 732 CKEDITOR.tools.setTimeout( function() 733 { 734 var viewSize = CKEDITOR.document.getWindow().getViewPaneSize(); 735 var dialogSize = this.getSize(); 736 737 // We're using definition size for initial position because of 738 // offten corrupted data in offsetWidth at this point. (#4084) 739 this.move( ( viewSize.width - definition.minWidth ) / 2, ( viewSize.height - dialogSize.height ) / 2 ); 740 741 this.parts.dialog.setStyle( 'visibility', '' ); 742 743 // Execute onLoad for the first show. 744 this.fireOnce( 'load', {} ); 745 this.fire( 'show', {} ); 746 this._.editor.fire( 'dialogShow', this ); 747 748 // Save the initial values of the dialog. 749 this.foreach( function( contentObj ) { contentObj.setInitValue && contentObj.setInitValue(); } ); 750 751 }, 752 100, this ); 753 }, 754 755 /** 756 * Executes a function for each UI element. 757 * @param {Function} fn Function to execute for each UI element. 758 * @returns {CKEDITOR.dialog} The current dialog object. 759 */ 760 foreach : function( fn ) 761 { 762 for ( var i in this._.contents ) 763 { 764 for ( var j in this._.contents[i] ) 765 fn( this._.contents[i][j] ); 766 } 767 return this; 768 }, 769 770 /** 771 * Resets all input values in the dialog. 772 * @example 773 * dialogObj.reset(); 774 * @returns {CKEDITOR.dialog} The current dialog object. 775 */ 776 reset : (function() 777 { 778 var fn = function( widget ){ if ( widget.reset ) widget.reset( 1 ); }; 779 return function(){ this.foreach( fn ); return this; }; 780 })(), 781 782 setupContent : function() 783 { 784 var args = arguments; 785 this.foreach( function( widget ) 786 { 787 if ( widget.setup ) 788 widget.setup.apply( widget, args ); 789 }); 790 }, 791 792 commitContent : function() 793 { 794 var args = arguments; 795 this.foreach( function( widget ) 796 { 797 if ( widget.commit ) 798 widget.commit.apply( widget, args ); 799 }); 800 }, 801 802 /** 803 * Hides the dialog box. 804 * @example 805 * dialogObj.hide(); 806 */ 807 hide : function() 808 { 809 if ( !this.parts.dialog.isVisible() ) 810 return; 811 812 this.fire( 'hide', {} ); 813 this._.editor.fire( 'dialogHide', this ); 814 var element = this._.element; 815 element.setStyle( 'display', 'none' ); 816 this.parts.dialog.setStyle( 'visibility', 'hidden' ); 817 // Unregister all access keys associated with this dialog. 818 unregisterAccessKey( this ); 819 820 // Close any child(top) dialogs first. 821 while( CKEDITOR.dialog._.currentTop != this ) 822 CKEDITOR.dialog._.currentTop.hide(); 823 824 // Maintain dialog ordering and remove cover if needed. 825 if ( !this._.parentDialog ) 826 hideCover(); 827 else 828 { 829 var parentElement = this._.parentDialog.getElement().getFirst(); 830 parentElement.setStyle( 'z-index', parseInt( parentElement.$.style.zIndex, 10 ) + Math.floor( this._.editor.config.baseFloatZIndex / 2 ) ); 831 } 832 CKEDITOR.dialog._.currentTop = this._.parentDialog; 833 834 // Deduct or clear the z-index. 835 if ( !this._.parentDialog ) 836 { 837 CKEDITOR.dialog._.currentZIndex = null; 838 839 // Remove access key handlers. 840 element.removeListener( 'keydown', accessKeyDownHandler ); 841 element.removeListener( CKEDITOR.env.opera ? 'keypress' : 'keyup', accessKeyUpHandler ); 842 843 // Remove bubbling-prevention handler. (#4269) 844 for ( var event in { keyup :1, keydown :1, keypress :1 } ) 845 element.removeListener( event, preventKeyBubbling ); 846 847 var editor = this._.editor; 848 editor.focus(); 849 850 if ( editor.mode == 'wysiwyg' && CKEDITOR.env.ie ) 851 { 852 var selection = editor.getSelection(); 853 selection && selection.unlock( true ); 854 } 855 } 856 else 857 CKEDITOR.dialog._.currentZIndex -= 10; 858 859 delete this._.parentDialog; 860 // Reset the initial values of the dialog. 861 this.foreach( function( contentObj ) { contentObj.resetInitValue && contentObj.resetInitValue(); } ); 862 }, 863 864 /** 865 * Adds a tabbed page into the dialog. 866 * @param {Object} contents Content definition. 867 * @example 868 */ 869 addPage : function( contents ) 870 { 871 var pageHtml = [], 872 titleHtml = contents.label ? ' title="' + CKEDITOR.tools.htmlEncode( contents.label ) + '"' : '', 873 elements = contents.elements, 874 vbox = CKEDITOR.dialog._.uiElementBuilders.vbox.build( this, 875 { 876 type : 'vbox', 877 className : 'cke_dialog_page_contents', 878 children : contents.elements, 879 expand : !!contents.expand, 880 padding : contents.padding, 881 style : contents.style || 'width: 100%; height: 100%;' 882 }, pageHtml ); 883 884 // Create the HTML for the tab and the content block. 885 var page = CKEDITOR.dom.element.createFromHtml( pageHtml.join( '' ) ); 886 page.setAttribute( 'role', 'tabpanel' ); 887 888 var env = CKEDITOR.env; 889 var tabId = 'cke_' + contents.id + '_' + CKEDITOR.tools.getNextNumber(), 890 tab = CKEDITOR.dom.element.createFromHtml( [ 891 '<a class="cke_dialog_tab"', 892 ( this._.pageCount > 0 ? ' cke_last' : 'cke_first' ), 893 titleHtml, 894 ( !!contents.hidden ? ' style="display:none"' : '' ), 895 ' id="', tabId, '"', 896 env.gecko && env.version >= 10900 && !env.hc ? '' : ' href="javascript:void(0)"', 897 ' tabIndex="-1"', 898 ' hidefocus="true"', 899 ' role="tab">', 900 contents.label, 901 '</a>' 902 ].join( '' ) ); 903 904 page.setAttribute( 'aria-labelledby', tabId ); 905 906 // Take records for the tabs and elements created. 907 this._.tabs[ contents.id ] = [ tab, page ]; 908 this._.tabIdList.push( contents.id ); 909 !contents.hidden && this._.pageCount++; 910 this._.lastTab = tab; 911 this.updateStyle(); 912 913 var contentMap = this._.contents[ contents.id ] = {}, 914 cursor, 915 children = vbox.getChild(); 916 917 while ( ( cursor = children.shift() ) ) 918 { 919 contentMap[ cursor.id ] = cursor; 920 if ( typeof( cursor.getChild ) == 'function' ) 921 children.push.apply( children, cursor.getChild() ); 922 } 923 924 // Attach the DOM nodes. 925 926 page.setAttribute( 'name', contents.id ); 927 page.appendTo( this.parts.contents ); 928 929 tab.unselectable(); 930 this.parts.tabs.append( tab ); 931 932 // Add access key handlers if access key is defined. 933 if ( contents.accessKey ) 934 { 935 registerAccessKey( this, this, 'CTRL+' + contents.accessKey, 936 tabAccessKeyDown, tabAccessKeyUp ); 937 this._.accessKeyMap[ 'CTRL+' + contents.accessKey ] = contents.id; 938 } 939 }, 940 941 /** 942 * Activates a tab page in the dialog by its id. 943 * @param {String} id The id of the dialog tab to be activated. 944 * @example 945 * dialogObj.selectPage( 'tab_1' ); 946 */ 947 selectPage : function( id ) 948 { 949 if ( this._.currentTabId == id ) 950 return; 951 952 // Returning true means that the event has been canceled 953 if ( this.fire( 'selectPage', { page : id, currentPage : this._.currentTabId } ) === true ) 954 return; 955 956 // Hide the non-selected tabs and pages. 957 for ( var i in this._.tabs ) 958 { 959 var tab = this._.tabs[i][0], 960 page = this._.tabs[i][1]; 961 if ( i != id ) 962 { 963 tab.removeClass( 'cke_dialog_tab_selected' ); 964 page.hide(); 965 } 966 page.setAttribute( 'aria-hidden', i != id ); 967 } 968 969 var selected = this._.tabs[id]; 970 selected[0].addClass( 'cke_dialog_tab_selected' ); 971 selected[1].show(); 972 this._.currentTabId = id; 973 this._.currentTabIndex = CKEDITOR.tools.indexOf( this._.tabIdList, id ); 974 }, 975 976 // Dialog state-specific style updates. 977 updateStyle : function() 978 { 979 // If only a single page shown, a different style is used in the central pane. 980 this.parts.dialog[ ( this._.pageCount === 1 ? 'add' : 'remove' ) + 'Class' ]( 'cke_single_page' ); 981 }, 982 983 /** 984 * Hides a page's tab away from the dialog. 985 * @param {String} id The page's Id. 986 * @example 987 * dialog.hidePage( 'tab_3' ); 988 */ 989 hidePage : function( id ) 990 { 991 var tab = this._.tabs[id] && this._.tabs[id][0]; 992 if ( !tab || this._.pageCount == 1 ) 993 return; 994 // Switch to other tab first when we're hiding the active tab. 995 else if ( id == this._.currentTabId ) 996 this.selectPage( getPreviousVisibleTab.call( this ) ); 997 998 tab.hide(); 999 this._.pageCount--; 1000 this.updateStyle(); 1001 }, 1002 1003 /** 1004 * Unhides a page's tab. 1005 * @param {String} id The page's Id. 1006 * @example 1007 * dialog.showPage( 'tab_2' ); 1008 */ 1009 showPage : function( id ) 1010 { 1011 var tab = this._.tabs[id] && this._.tabs[id][0]; 1012 if ( !tab ) 1013 return; 1014 tab.show(); 1015 this._.pageCount++; 1016 this.updateStyle(); 1017 }, 1018 1019 /** 1020 * Gets the root DOM element of the dialog. 1021 * @returns {CKEDITOR.dom.element} The <span> element containing this dialog. 1022 * @example 1023 * var dialogElement = dialogObj.getElement().getFirst(); 1024 * dialogElement.setStyle( 'padding', '5px' ); 1025 */ 1026 getElement : function() 1027 { 1028 return this._.element; 1029 }, 1030 1031 /** 1032 * Gets the name of the dialog. 1033 * @returns {String} The name of this dialog. 1034 * @example 1035 * var dialogName = dialogObj.getName(); 1036 */ 1037 getName : function() 1038 { 1039 return this._.name; 1040 }, 1041 1042 /** 1043 * Gets a dialog UI element object from a dialog page. 1044 * @param {String} pageId id of dialog page. 1045 * @param {String} elementId id of UI element. 1046 * @example 1047 * @returns {CKEDITOR.ui.dialog.uiElement} The dialog UI element. 1048 */ 1049 getContentElement : function( pageId, elementId ) 1050 { 1051 var page = this._.contents[ pageId ]; 1052 return page && page[ elementId ]; 1053 }, 1054 1055 /** 1056 * Gets the value of a dialog UI element. 1057 * @param {String} pageId id of dialog page. 1058 * @param {String} elementId id of UI element. 1059 * @example 1060 * @returns {Object} The value of the UI element. 1061 */ 1062 getValueOf : function( pageId, elementId ) 1063 { 1064 return this.getContentElement( pageId, elementId ).getValue(); 1065 }, 1066 1067 /** 1068 * Sets the value of a dialog UI element. 1069 * @param {String} pageId id of the dialog page. 1070 * @param {String} elementId id of the UI element. 1071 * @param {Object} value The new value of the UI element. 1072 * @example 1073 */ 1074 setValueOf : function( pageId, elementId, value ) 1075 { 1076 return this.getContentElement( pageId, elementId ).setValue( value ); 1077 }, 1078 1079 /** 1080 * Gets the UI element of a button in the dialog's button row. 1081 * @param {String} id The id of the button. 1082 * @example 1083 * @returns {CKEDITOR.ui.dialog.button} The button object. 1084 */ 1085 getButton : function( id ) 1086 { 1087 return this._.buttons[ id ]; 1088 }, 1089 1090 /** 1091 * Simulates a click to a dialog button in the dialog's button row. 1092 * @param {String} id The id of the button. 1093 * @example 1094 * @returns The return value of the dialog's "click" event. 1095 */ 1096 click : function( id ) 1097 { 1098 return this._.buttons[ id ].click(); 1099 }, 1100 1101 /** 1102 * Disables a dialog button. 1103 * @param {String} id The id of the button. 1104 * @example 1105 */ 1106 disableButton : function( id ) 1107 { 1108 return this._.buttons[ id ].disable(); 1109 }, 1110 1111 /** 1112 * Enables a dialog button. 1113 * @param {String} id The id of the button. 1114 * @example 1115 */ 1116 enableButton : function( id ) 1117 { 1118 return this._.buttons[ id ].enable(); 1119 }, 1120 1121 /** 1122 * Gets the number of pages in the dialog. 1123 * @returns {Number} Page count. 1124 */ 1125 getPageCount : function() 1126 { 1127 return this._.pageCount; 1128 }, 1129 1130 /** 1131 * Gets the editor instance which opened this dialog. 1132 * @returns {CKEDITOR.editor} Parent editor instances. 1133 */ 1134 getParentEditor : function() 1135 { 1136 return this._.editor; 1137 }, 1138 1139 /** 1140 * Gets the element that was selected when opening the dialog, if any. 1141 * @returns {CKEDITOR.dom.element} The element that was selected, or null. 1142 */ 1143 getSelectedElement : function() 1144 { 1145 return this.getParentEditor().getSelection().getSelectedElement(); 1146 }, 1147 1148 /** 1149 * Adds element to dialog's focusable list. 1150 * 1151 * @param {CKEDITOR.dom.element} element 1152 * @param {Number} [index] 1153 */ 1154 addFocusable: function( element, index ) { 1155 if ( typeof index == 'undefined' ) 1156 { 1157 index = this._.focusList.length; 1158 this._.focusList.push( new Focusable( this, element, index ) ); 1159 } 1160 else 1161 { 1162 this._.focusList.splice( index, 0, new Focusable( this, element, index ) ); 1163 for ( var i = index + 1 ; i < this._.focusList.length ; i++ ) 1164 this._.focusList[ i ].focusIndex++; 1165 } 1166 } 1167 }; 1168 1169 CKEDITOR.tools.extend( CKEDITOR.dialog, 1170 /** 1171 * @lends CKEDITOR.dialog 1172 */ 1173 { 1174 /** 1175 * Registers a dialog. 1176 * @param {String} name The dialog's name. 1177 * @param {Function|String} dialogDefinition 1178 * A function returning the dialog's definition, or the URL to the .js file holding the function. 1179 * The function should accept an argument "editor" which is the current editor instance, and 1180 * return an object conforming to {@link CKEDITOR.dialog.dialogDefinition}. 1181 * @example 1182 * @see CKEDITOR.dialog.dialogDefinition 1183 */ 1184 add : function( name, dialogDefinition ) 1185 { 1186 // Avoid path registration from multiple instances override definition. 1187 if ( !this._.dialogDefinitions[name] 1188 || typeof dialogDefinition == 'function' ) 1189 this._.dialogDefinitions[name] = dialogDefinition; 1190 }, 1191 1192 exists : function( name ) 1193 { 1194 return !!this._.dialogDefinitions[ name ]; 1195 }, 1196 1197 getCurrent : function() 1198 { 1199 return CKEDITOR.dialog._.currentTop; 1200 }, 1201 1202 /** 1203 * The default OK button for dialogs. Fires the "ok" event and closes the dialog if the event succeeds. 1204 * @static 1205 * @field 1206 * @example 1207 * @type Function 1208 */ 1209 okButton : (function() 1210 { 1211 var retval = function( editor, override ) 1212 { 1213 override = override || {}; 1214 return CKEDITOR.tools.extend( { 1215 id : 'ok', 1216 type : 'button', 1217 label : editor.lang.common.ok, 1218 'class' : 'cke_dialog_ui_button_ok', 1219 onClick : function( evt ) 1220 { 1221 var dialog = evt.data.dialog; 1222 if ( dialog.fire( 'ok', { hide : true } ).hide !== false ) 1223 dialog.hide(); 1224 } 1225 }, override, true ); 1226 }; 1227 retval.type = 'button'; 1228 retval.override = function( override ) 1229 { 1230 return CKEDITOR.tools.extend( function( editor ){ return retval( editor, override ); }, 1231 { type : 'button' }, true ); 1232 }; 1233 return retval; 1234 })(), 1235 1236 /** 1237 * The default cancel button for dialogs. Fires the "cancel" event and closes the dialog if no UI element value changed. 1238 * @static 1239 * @field 1240 * @example 1241 * @type Function 1242 */ 1243 cancelButton : (function() 1244 { 1245 var retval = function( editor, override ) 1246 { 1247 override = override || {}; 1248 return CKEDITOR.tools.extend( { 1249 id : 'cancel', 1250 type : 'button', 1251 label : editor.lang.common.cancel, 1252 'class' : 'cke_dialog_ui_button_cancel', 1253 onClick : function( evt ) 1254 { 1255 var dialog = evt.data.dialog; 1256 if ( dialog.fire( 'cancel', { hide : true } ).hide !== false ) 1257 dialog.hide(); 1258 } 1259 }, override, true ); 1260 }; 1261 retval.type = 'button'; 1262 retval.override = function( override ) 1263 { 1264 return CKEDITOR.tools.extend( function( editor ){ return retval( editor, override ); }, 1265 { type : 'button' }, true ); 1266 }; 1267 return retval; 1268 })(), 1269 1270 /** 1271 * Registers a dialog UI element. 1272 * @param {String} typeName The name of the UI element. 1273 * @param {Function} builder The function to build the UI element. 1274 * @example 1275 */ 1276 addUIElement : function( typeName, builder ) 1277 { 1278 this._.uiElementBuilders[ typeName ] = builder; 1279 } 1280 }); 1281 1282 CKEDITOR.dialog._ = 1283 { 1284 uiElementBuilders : {}, 1285 1286 dialogDefinitions : {}, 1287 1288 currentTop : null, 1289 1290 currentZIndex : null 1291 }; 1292 1293 // "Inherit" (copy actually) from CKEDITOR.event. 1294 CKEDITOR.event.implementOn( CKEDITOR.dialog ); 1295 CKEDITOR.event.implementOn( CKEDITOR.dialog.prototype, true ); 1296 1297 var defaultDialogDefinition = 1298 { 1299 resizable : CKEDITOR.DIALOG_RESIZE_BOTH, 1300 minWidth : 600, 1301 minHeight : 400, 1302 buttons : [ CKEDITOR.dialog.okButton, CKEDITOR.dialog.cancelButton ] 1303 }; 1304 1305 // The buttons in MacOS Apps are in reverse order #4750 1306 CKEDITOR.env.mac && defaultDialogDefinition.buttons.reverse(); 1307 1308 // Tool function used to return an item from an array based on its id 1309 // property. 1310 var getById = function( array, id, recurse ) 1311 { 1312 for ( var i = 0, item ; ( item = array[ i ] ) ; i++ ) 1313 { 1314 if ( item.id == id ) 1315 return item; 1316 if ( recurse && item[ recurse ] ) 1317 { 1318 var retval = getById( item[ recurse ], id, recurse ) ; 1319 if ( retval ) 1320 return retval; 1321 } 1322 } 1323 return null; 1324 }; 1325 1326 // Tool function used to add an item into an array. 1327 var addById = function( array, newItem, nextSiblingId, recurse, nullIfNotFound ) 1328 { 1329 if ( nextSiblingId ) 1330 { 1331 for ( var i = 0, item ; ( item = array[ i ] ) ; i++ ) 1332 { 1333 if ( item.id == nextSiblingId ) 1334 { 1335 array.splice( i, 0, newItem ); 1336 return newItem; 1337 } 1338 1339 if ( recurse && item[ recurse ] ) 1340 { 1341 var retval = addById( item[ recurse ], newItem, nextSiblingId, recurse, true ); 1342 if ( retval ) 1343 return retval; 1344 } 1345 } 1346 1347 if ( nullIfNotFound ) 1348 return null; 1349 } 1350 1351 array.push( newItem ); 1352 return newItem; 1353 }; 1354 1355 // Tool function used to remove an item from an array based on its id. 1356 var removeById = function( array, id, recurse ) 1357 { 1358 for ( var i = 0, item ; ( item = array[ i ] ) ; i++ ) 1359 { 1360 if ( item.id == id ) 1361 return array.splice( i, 1 ); 1362 if ( recurse && item[ recurse ] ) 1363 { 1364 var retval = removeById( item[ recurse ], id, recurse ); 1365 if ( retval ) 1366 return retval; 1367 } 1368 } 1369 return null; 1370 }; 1371 1372 /** 1373 * This class is not really part of the API. It is the "definition" property value 1374 * passed to "dialogDefinition" event handlers. 1375 * @constructor 1376 * @name CKEDITOR.dialog.dialogDefinitionObject 1377 * @extends CKEDITOR.dialog.dialogDefinition 1378 * @example 1379 * CKEDITOR.on( 'dialogDefinition', function( evt ) 1380 * { 1381 * var definition = evt.data.definition; 1382 * var content = definition.getContents( 'page1' ); 1383 * ... 1384 * } ); 1385 */ 1386 var definitionObject = function( dialog, dialogDefinition ) 1387 { 1388 // TODO : Check if needed. 1389 this.dialog = dialog; 1390 1391 // Transform the contents entries in contentObjects. 1392 var contents = dialogDefinition.contents; 1393 for ( var i = 0, content ; ( content = contents[i] ) ; i++ ) 1394 contents[ i ] = content && new contentObject( dialog, content ); 1395 1396 CKEDITOR.tools.extend( this, dialogDefinition ); 1397 }; 1398 1399 definitionObject.prototype = 1400 /** @lends CKEDITOR.dialog.dialogDefinitionObject.prototype */ 1401 { 1402 /** 1403 * Gets a content definition. 1404 * @param {String} id The id of the content definition. 1405 * @returns {CKEDITOR.dialog.contentDefinition} The content definition 1406 * matching id. 1407 */ 1408 getContents : function( id ) 1409 { 1410 return getById( this.contents, id ); 1411 }, 1412 1413 /** 1414 * Gets a button definition. 1415 * @param {String} id The id of the button definition. 1416 * @returns {CKEDITOR.dialog.buttonDefinition} The button definition 1417 * matching id. 1418 */ 1419 getButton : function( id ) 1420 { 1421 return getById( this.buttons, id ); 1422 }, 1423 1424 /** 1425 * Adds a content definition object under this dialog definition. 1426 * @param {CKEDITOR.dialog.contentDefinition} contentDefinition The 1427 * content definition. 1428 * @param {String} [nextSiblingId] The id of an existing content 1429 * definition which the new content definition will be inserted 1430 * before. Omit if the new content definition is to be inserted as 1431 * the last item. 1432 * @returns {CKEDITOR.dialog.contentDefinition} The inserted content 1433 * definition. 1434 */ 1435 addContents : function( contentDefinition, nextSiblingId ) 1436 { 1437 return addById( this.contents, contentDefinition, nextSiblingId ); 1438 }, 1439 1440 /** 1441 * Adds a button definition object under this dialog definition. 1442 * @param {CKEDITOR.dialog.buttonDefinition} buttonDefinition The 1443 * button definition. 1444 * @param {String} [nextSiblingId] The id of an existing button 1445 * definition which the new button definition will be inserted 1446 * before. Omit if the new button definition is to be inserted as 1447 * the last item. 1448 * @returns {CKEDITOR.dialog.buttonDefinition} The inserted button 1449 * definition. 1450 */ 1451 addButton : function( buttonDefinition, nextSiblingId ) 1452 { 1453 return addById( this.buttons, buttonDefinition, nextSiblingId ); 1454 }, 1455 1456 /** 1457 * Removes a content definition from this dialog definition. 1458 * @param {String} id The id of the content definition to be removed. 1459 * @returns {CKEDITOR.dialog.contentDefinition} The removed content 1460 * definition. 1461 */ 1462 removeContents : function( id ) 1463 { 1464 removeById( this.contents, id ); 1465 }, 1466 1467 /** 1468 * Removes a button definition from the dialog definition. 1469 * @param {String} id The id of the button definition to be removed. 1470 * @returns {CKEDITOR.dialog.buttonDefinition} The removed button 1471 * definition. 1472 */ 1473 removeButton : function( id ) 1474 { 1475 removeById( this.buttons, id ); 1476 } 1477 }; 1478 1479 /** 1480 * This class is not really part of the API. It is the template of the 1481 * objects representing content pages inside the 1482 * CKEDITOR.dialog.dialogDefinitionObject. 1483 * @constructor 1484 * @name CKEDITOR.dialog.contentDefinitionObject 1485 * @example 1486 * CKEDITOR.on( 'dialogDefinition', function( evt ) 1487 * { 1488 * var definition = evt.data.definition; 1489 * var content = definition.getContents( 'page1' ); 1490 * content.remove( 'textInput1' ); 1491 * ... 1492 * } ); 1493 */ 1494 function contentObject( dialog, contentDefinition ) 1495 { 1496 this._ = 1497 { 1498 dialog : dialog 1499 }; 1500 1501 CKEDITOR.tools.extend( this, contentDefinition ); 1502 } 1503 1504 contentObject.prototype = 1505 /** @lends CKEDITOR.dialog.contentDefinitionObject.prototype */ 1506 { 1507 /** 1508 * Gets a UI element definition under the content definition. 1509 * @param {String} id The id of the UI element definition. 1510 * @returns {CKEDITOR.dialog.uiElementDefinition} 1511 */ 1512 get : function( id ) 1513 { 1514 return getById( this.elements, id, 'children' ); 1515 }, 1516 1517 /** 1518 * Adds a UI element definition to the content definition. 1519 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition The 1520 * UI elemnet definition to be added. 1521 * @param {String} nextSiblingId The id of an existing UI element 1522 * definition which the new UI element definition will be inserted 1523 * before. Omit if the new button definition is to be inserted as 1524 * the last item. 1525 * @returns {CKEDITOR.dialog.uiElementDefinition} The element 1526 * definition inserted. 1527 */ 1528 add : function( elementDefinition, nextSiblingId ) 1529 { 1530 return addById( this.elements, elementDefinition, nextSiblingId, 'children' ); 1531 }, 1532 1533 /** 1534 * Removes a UI element definition from the content definition. 1535 * @param {String} id The id of the UI element definition to be 1536 * removed. 1537 * @returns {CKEDITOR.dialog.uiElementDefinition} The element 1538 * definition removed. 1539 * @example 1540 */ 1541 remove : function( id ) 1542 { 1543 removeById( this.elements, id, 'children' ); 1544 } 1545 }; 1546 1547 function initDragAndDrop( dialog ) 1548 { 1549 var lastCoords = null, 1550 abstractDialogCoords = null, 1551 element = dialog.getElement().getFirst(), 1552 editor = dialog.getParentEditor(), 1553 magnetDistance = editor.config.dialog_magnetDistance, 1554 margins = editor.skin.margins || [ 0, 0, 0, 0 ]; 1555 1556 if ( typeof magnetDistance == 'undefined' ) 1557 magnetDistance = 20; 1558 1559 function mouseMoveHandler( evt ) 1560 { 1561 var dialogSize = dialog.getSize(), 1562 viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(), 1563 x = evt.data.$.screenX, 1564 y = evt.data.$.screenY, 1565 dx = x - lastCoords.x, 1566 dy = y - lastCoords.y, 1567 realX, realY; 1568 1569 lastCoords = { x : x, y : y }; 1570 abstractDialogCoords.x += dx; 1571 abstractDialogCoords.y += dy; 1572 1573 if ( abstractDialogCoords.x + margins[3] < magnetDistance ) 1574 realX = - margins[3]; 1575 else if ( abstractDialogCoords.x - margins[1] > viewPaneSize.width - dialogSize.width - magnetDistance ) 1576 realX = viewPaneSize.width - dialogSize.width + margins[1]; 1577 else 1578 realX = abstractDialogCoords.x; 1579 1580 if ( abstractDialogCoords.y + margins[0] < magnetDistance ) 1581 realY = - margins[0]; 1582 else if ( abstractDialogCoords.y - margins[2] > viewPaneSize.height - dialogSize.height - magnetDistance ) 1583 realY = viewPaneSize.height - dialogSize.height + margins[2]; 1584 else 1585 realY = abstractDialogCoords.y; 1586 1587 dialog.move( realX, realY ); 1588 1589 evt.data.preventDefault(); 1590 } 1591 1592 function mouseUpHandler( evt ) 1593 { 1594 CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler ); 1595 CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler ); 1596 1597 if ( CKEDITOR.env.ie6Compat ) 1598 { 1599 var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); 1600 coverDoc.removeListener( 'mousemove', mouseMoveHandler ); 1601 coverDoc.removeListener( 'mouseup', mouseUpHandler ); 1602 } 1603 } 1604 1605 dialog.parts.title.on( 'mousedown', function( evt ) 1606 { 1607 dialog._.updateSize = true; 1608 1609 lastCoords = { x : evt.data.$.screenX, y : evt.data.$.screenY }; 1610 1611 CKEDITOR.document.on( 'mousemove', mouseMoveHandler ); 1612 CKEDITOR.document.on( 'mouseup', mouseUpHandler ); 1613 abstractDialogCoords = dialog.getPosition(); 1614 1615 if ( CKEDITOR.env.ie6Compat ) 1616 { 1617 var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); 1618 coverDoc.on( 'mousemove', mouseMoveHandler ); 1619 coverDoc.on( 'mouseup', mouseUpHandler ); 1620 } 1621 1622 evt.data.preventDefault(); 1623 }, dialog ); 1624 } 1625 1626 function initResizeHandles( dialog ) 1627 { 1628 var definition = dialog.definition, 1629 minWidth = definition.minWidth || 0, 1630 minHeight = definition.minHeight || 0, 1631 resizable = definition.resizable, 1632 margins = dialog.getParentEditor().skin.margins || [ 0, 0, 0, 0 ]; 1633 1634 function topSizer( coords, dy ) 1635 { 1636 coords.y += dy; 1637 } 1638 1639 function rightSizer( coords, dx ) 1640 { 1641 coords.x2 += dx; 1642 } 1643 1644 function bottomSizer( coords, dy ) 1645 { 1646 coords.y2 += dy; 1647 } 1648 1649 function leftSizer( coords, dx ) 1650 { 1651 coords.x += dx; 1652 } 1653 1654 var lastCoords = null, 1655 abstractDialogCoords = null, 1656 magnetDistance = dialog._.editor.config.magnetDistance, 1657 parts = [ 'tl', 't', 'tr', 'l', 'r', 'bl', 'b', 'br' ]; 1658 1659 function mouseDownHandler( evt ) 1660 { 1661 var partName = evt.listenerData.part, size = dialog.getSize(); 1662 abstractDialogCoords = dialog.getPosition(); 1663 CKEDITOR.tools.extend( abstractDialogCoords, 1664 { 1665 x2 : abstractDialogCoords.x + size.width, 1666 y2 : abstractDialogCoords.y + size.height 1667 } ); 1668 lastCoords = { x : evt.data.$.screenX, y : evt.data.$.screenY }; 1669 1670 CKEDITOR.document.on( 'mousemove', mouseMoveHandler, dialog, { part : partName } ); 1671 CKEDITOR.document.on( 'mouseup', mouseUpHandler, dialog, { part : partName } ); 1672 1673 if ( CKEDITOR.env.ie6Compat ) 1674 { 1675 var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); 1676 coverDoc.on( 'mousemove', mouseMoveHandler, dialog, { part : partName } ); 1677 coverDoc.on( 'mouseup', mouseUpHandler, dialog, { part : partName } ); 1678 } 1679 1680 evt.data.preventDefault(); 1681 } 1682 1683 function mouseMoveHandler( evt ) 1684 { 1685 var x = evt.data.$.screenX, 1686 y = evt.data.$.screenY, 1687 dx = x - lastCoords.x, 1688 dy = y - lastCoords.y, 1689 viewPaneSize = CKEDITOR.document.getWindow().getViewPaneSize(), 1690 partName = evt.listenerData.part; 1691 1692 if ( partName.search( 't' ) != -1 ) 1693 topSizer( abstractDialogCoords, dy ); 1694 if ( partName.search( 'l' ) != -1 ) 1695 leftSizer( abstractDialogCoords, dx ); 1696 if ( partName.search( 'b' ) != -1 ) 1697 bottomSizer( abstractDialogCoords, dy ); 1698 if ( partName.search( 'r' ) != -1 ) 1699 rightSizer( abstractDialogCoords, dx ); 1700 1701 lastCoords = { x : x, y : y }; 1702 1703 var realX, realY, realX2, realY2; 1704 1705 if ( abstractDialogCoords.x + margins[3] < magnetDistance ) 1706 realX = - margins[3]; 1707 else if ( partName.search( 'l' ) != -1 && abstractDialogCoords.x2 - abstractDialogCoords.x < minWidth + magnetDistance ) 1708 realX = abstractDialogCoords.x2 - minWidth; 1709 else 1710 realX = abstractDialogCoords.x; 1711 1712 if ( abstractDialogCoords.y + margins[0] < magnetDistance ) 1713 realY = - margins[0]; 1714 else if ( partName.search( 't' ) != -1 && abstractDialogCoords.y2 - abstractDialogCoords.y < minHeight + magnetDistance ) 1715 realY = abstractDialogCoords.y2 - minHeight; 1716 else 1717 realY = abstractDialogCoords.y; 1718 1719 if ( abstractDialogCoords.x2 - margins[1] > viewPaneSize.width - magnetDistance ) 1720 realX2 = viewPaneSize.width + margins[1] ; 1721 else if ( partName.search( 'r' ) != -1 && abstractDialogCoords.x2 - abstractDialogCoords.x < minWidth + magnetDistance ) 1722 realX2 = abstractDialogCoords.x + minWidth; 1723 else 1724 realX2 = abstractDialogCoords.x2; 1725 1726 if ( abstractDialogCoords.y2 - margins[2] > viewPaneSize.height - magnetDistance ) 1727 realY2= viewPaneSize.height + margins[2] ; 1728 else if ( partName.search( 'b' ) != -1 && abstractDialogCoords.y2 - abstractDialogCoords.y < minHeight + magnetDistance ) 1729 realY2 = abstractDialogCoords.y + minHeight; 1730 else 1731 realY2 = abstractDialogCoords.y2 ; 1732 1733 dialog.move( realX, realY ); 1734 dialog.resize( realX2 - realX, realY2 - realY ); 1735 1736 evt.data.preventDefault(); 1737 } 1738 1739 function mouseUpHandler( evt ) 1740 { 1741 CKEDITOR.document.removeListener( 'mouseup', mouseUpHandler ); 1742 CKEDITOR.document.removeListener( 'mousemove', mouseMoveHandler ); 1743 1744 if ( CKEDITOR.env.ie6Compat ) 1745 { 1746 var coverDoc = currentCover.getChild( 0 ).getFrameDocument(); 1747 coverDoc.removeListener( 'mouseup', mouseUpHandler ); 1748 coverDoc.removeListener( 'mousemove', mouseMoveHandler ); 1749 } 1750 } 1751 1752 // TODO : Simplify the resize logic, having just a single resize grip <div>. 1753 // var widthTest = /[lr]/, 1754 // heightTest = /[tb]/; 1755 // for ( var i = 0 ; i < parts.length ; i++ ) 1756 // { 1757 // var element = dialog.parts[ parts[i] + '_resize' ]; 1758 // if ( resizable == CKEDITOR.DIALOG_RESIZE_NONE || 1759 // resizable == CKEDITOR.DIALOG_RESIZE_HEIGHT && widthTest.test( parts[i] ) || 1760 // resizable == CKEDITOR.DIALOG_RESIZE_WIDTH && heightTest.test( parts[i] ) ) 1761 // { 1762 // element.hide(); 1763 // continue; 1764 // } 1765 // element.on( 'mousedown', mouseDownHandler, dialog, { part : parts[i] } ); 1766 // } 1767 } 1768 1769 var resizeCover; 1770 // Caching resuable covers and allowing only one cover 1771 // on screen. 1772 var covers = {}, 1773 currentCover; 1774 1775 function showCover( editor ) 1776 { 1777 var win = CKEDITOR.document.getWindow(); 1778 var backgroundColorStyle = editor.config.dialog_backgroundCoverColor || 'white', 1779 backgroundCoverOpacity = editor.config.dialog_backgroundCoverOpacity, 1780 baseFloatZIndex = editor.config.baseFloatZIndex, 1781 coverKey = CKEDITOR.tools.genKey( 1782 backgroundColorStyle, 1783 backgroundCoverOpacity, 1784 baseFloatZIndex ), 1785 coverElement = covers[ coverKey ]; 1786 1787 if ( !coverElement ) 1788 { 1789 var html = [ 1790 '<div style="position: ', ( CKEDITOR.env.ie6Compat ? 'absolute' : 'fixed' ), 1791 '; z-index: ', baseFloatZIndex, 1792 '; top: 0px; left: 0px; ', 1793 ( !CKEDITOR.env.ie6Compat ? 'background-color: ' + backgroundColorStyle : '' ), 1794 '" class="cke_dialog_background_cover">' 1795 ]; 1796 1797 if ( CKEDITOR.env.ie6Compat ) 1798 { 1799 // Support for custom document.domain in IE. 1800 var isCustomDomain = CKEDITOR.env.isCustomDomain(), 1801 iframeHtml = '<html><body style=\\\'background-color:' + backgroundColorStyle + ';\\\'></body></html>'; 1802 1803 html.push( 1804 '<iframe' + 1805 ' hidefocus="true"' + 1806 ' frameborder="0"' + 1807 ' id="cke_dialog_background_iframe"' + 1808 ' src="javascript:' ); 1809 1810 html.push( 'void((function(){' + 1811 'document.open();' + 1812 ( isCustomDomain ? 'document.domain=\'' + document.domain + '\';' : '' ) + 1813 'document.write( \'' + iframeHtml + '\' );' + 1814 'document.close();' + 1815 '})())' ); 1816 1817 html.push( 1818 '"' + 1819 ' style="' + 1820 'position:absolute;' + 1821 'left:0;' + 1822 'top:0;' + 1823 'width:100%;' + 1824 'height: 100%;' + 1825 'progid:DXImageTransform.Microsoft.Alpha(opacity=0)">' + 1826 '</iframe>' ); 1827 } 1828 1829 html.push( '</div>' ); 1830 1831 coverElement = CKEDITOR.dom.element.createFromHtml( html.join( '' ) ); 1832 coverElement.setOpacity( backgroundCoverOpacity != undefined ? backgroundCoverOpacity : 0.5 ); 1833 1834 coverElement.appendTo( CKEDITOR.document.getBody() ); 1835 covers[ coverKey ] = coverElement; 1836 } 1837 else 1838 coverElement. show(); 1839 1840 currentCover = coverElement; 1841 var resizeFunc = function() 1842 { 1843 var size = win.getViewPaneSize(); 1844 coverElement.setStyles( 1845 { 1846 width : size.width + 'px', 1847 height : size.height + 'px' 1848 } ); 1849 }; 1850 1851 var scrollFunc = function() 1852 { 1853 var pos = win.getScrollPosition(), 1854 cursor = CKEDITOR.dialog._.currentTop; 1855 coverElement.setStyles( 1856 { 1857 left : pos.x + 'px', 1858 top : pos.y + 'px' 1859 }); 1860 1861 do 1862 { 1863 var dialogPos = cursor.getPosition(); 1864 cursor.move( dialogPos.x, dialogPos.y ); 1865 } while ( ( cursor = cursor._.parentDialog ) ); 1866 }; 1867 1868 resizeCover = resizeFunc; 1869 win.on( 'resize', resizeFunc ); 1870 resizeFunc(); 1871 if ( CKEDITOR.env.ie6Compat ) 1872 { 1873 // IE BUG: win.$.onscroll assignment doesn't work.. it must be window.onscroll. 1874 // So we need to invent a really funny way to make it work. 1875 var myScrollHandler = function() 1876 { 1877 scrollFunc(); 1878 arguments.callee.prevScrollHandler.apply( this, arguments ); 1879 }; 1880 win.$.setTimeout( function() 1881 { 1882 myScrollHandler.prevScrollHandler = window.onscroll || function(){}; 1883 window.onscroll = myScrollHandler; 1884 }, 0 ); 1885 scrollFunc(); 1886 } 1887 } 1888 1889 function hideCover() 1890 { 1891 if ( !currentCover ) 1892 return; 1893 1894 var win = CKEDITOR.document.getWindow(); 1895 currentCover.hide(); 1896 win.removeListener( 'resize', resizeCover ); 1897 1898 if ( CKEDITOR.env.ie6Compat ) 1899 { 1900 win.$.setTimeout( function() 1901 { 1902 var prevScrollHandler = window.onscroll && window.onscroll.prevScrollHandler; 1903 window.onscroll = prevScrollHandler || null; 1904 }, 0 ); 1905 } 1906 resizeCover = null; 1907 } 1908 1909 function removeCovers() 1910 { 1911 for ( var coverId in covers ) 1912 covers[ coverId ].remove(); 1913 covers = {}; 1914 } 1915 1916 var accessKeyProcessors = {}; 1917 1918 var accessKeyDownHandler = function( evt ) 1919 { 1920 var ctrl = evt.data.$.ctrlKey || evt.data.$.metaKey, 1921 alt = evt.data.$.altKey, 1922 shift = evt.data.$.shiftKey, 1923 key = String.fromCharCode( evt.data.$.keyCode ), 1924 keyProcessor = accessKeyProcessors[( ctrl ? 'CTRL+' : '' ) + ( alt ? 'ALT+' : '') + ( shift ? 'SHIFT+' : '' ) + key]; 1925 1926 if ( !keyProcessor || !keyProcessor.length ) 1927 return; 1928 1929 keyProcessor = keyProcessor[keyProcessor.length - 1]; 1930 keyProcessor.keydown && keyProcessor.keydown.call( keyProcessor.uiElement, keyProcessor.dialog, keyProcessor.key ); 1931 evt.data.preventDefault(); 1932 }; 1933 1934 var accessKeyUpHandler = function( evt ) 1935 { 1936 var ctrl = evt.data.$.ctrlKey || evt.data.$.metaKey, 1937 alt = evt.data.$.altKey, 1938 shift = evt.data.$.shiftKey, 1939 key = String.fromCharCode( evt.data.$.keyCode ), 1940 keyProcessor = accessKeyProcessors[( ctrl ? 'CTRL+' : '' ) + ( alt ? 'ALT+' : '') + ( shift ? 'SHIFT+' : '' ) + key]; 1941 1942 if ( !keyProcessor || !keyProcessor.length ) 1943 return; 1944 1945 keyProcessor = keyProcessor[keyProcessor.length - 1]; 1946 if ( keyProcessor.keyup ) 1947 { 1948 keyProcessor.keyup.call( keyProcessor.uiElement, keyProcessor.dialog, keyProcessor.key ); 1949 evt.data.preventDefault(); 1950 } 1951 }; 1952 1953 var registerAccessKey = function( uiElement, dialog, key, downFunc, upFunc ) 1954 { 1955 var procList = accessKeyProcessors[key] || ( accessKeyProcessors[key] = [] ); 1956 procList.push( { 1957 uiElement : uiElement, 1958 dialog : dialog, 1959 key : key, 1960 keyup : upFunc || uiElement.accessKeyUp, 1961 keydown : downFunc || uiElement.accessKeyDown 1962 } ); 1963 }; 1964 1965 var unregisterAccessKey = function( obj ) 1966 { 1967 for ( var i in accessKeyProcessors ) 1968 { 1969 var list = accessKeyProcessors[i]; 1970 for ( var j = list.length - 1 ; j >= 0 ; j-- ) 1971 { 1972 if ( list[j].dialog == obj || list[j].uiElement == obj ) 1973 list.splice( j, 1 ); 1974 } 1975 if ( list.length === 0 ) 1976 delete accessKeyProcessors[i]; 1977 } 1978 }; 1979 1980 var tabAccessKeyUp = function( dialog, key ) 1981 { 1982 if ( dialog._.accessKeyMap[key] ) 1983 dialog.selectPage( dialog._.accessKeyMap[key] ); 1984 }; 1985 1986 var tabAccessKeyDown = function( dialog, key ) 1987 { 1988 }; 1989 1990 // ESC, ENTER 1991 var preventKeyBubblingKeys = { 27 :1, 13 :1 }; 1992 var preventKeyBubbling = function( e ) 1993 { 1994 if ( e.data.getKeystroke() in preventKeyBubblingKeys ) 1995 e.data.stopPropagation(); 1996 }; 1997 1998 (function() 1999 { 2000 CKEDITOR.ui.dialog = 2001 { 2002 /** 2003 * The base class of all dialog UI elements. 2004 * @constructor 2005 * @param {CKEDITOR.dialog} dialog Parent dialog object. 2006 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition Element 2007 * definition. Accepted fields: 2008 * <ul> 2009 * <li><strong>id</strong> (Required) The id of the UI element. See {@link 2010 * CKEDITOR.dialog#getContentElement}</li> 2011 * <li><strong>type</strong> (Required) The type of the UI element. The 2012 * value to this field specifies which UI element class will be used to 2013 * generate the final widget.</li> 2014 * <li><strong>title</strong> (Optional) The popup tooltip for the UI 2015 * element.</li> 2016 * <li><strong>hidden</strong> (Optional) A flag that tells if the element 2017 * should be initially visible.</li> 2018 * <li><strong>className</strong> (Optional) Additional CSS class names 2019 * to add to the UI element. Separated by space.</li> 2020 * <li><strong>style</strong> (Optional) Additional CSS inline styles 2021 * to add to the UI element. A semicolon (;) is required after the last 2022 * style declaration.</li> 2023 * <li><strong>accessKey</strong> (Optional) The alphanumeric access key 2024 * for this element. Access keys are automatically prefixed by CTRL.</li> 2025 * <li><strong>on*</strong> (Optional) Any UI element definition field that 2026 * starts with <em>on</em> followed immediately by a capital letter and 2027 * probably more letters is an event handler. Event handlers may be further 2028 * divided into registered event handlers and DOM event handlers. Please 2029 * refer to {@link CKEDITOR.ui.dialog.uiElement#registerEvents} and 2030 * {@link CKEDITOR.ui.dialog.uiElement#eventProcessors} for more 2031 * information.</li> 2032 * </ul> 2033 * @param {Array} htmlList 2034 * List of HTML code to be added to the dialog's content area. 2035 * @param {Function|String} nodeNameArg 2036 * A function returning a string, or a simple string for the node name for 2037 * the root DOM node. Default is 'div'. 2038 * @param {Function|Object} stylesArg 2039 * A function returning an object, or a simple object for CSS styles applied 2040 * to the DOM node. Default is empty object. 2041 * @param {Function|Object} attributesArg 2042 * A fucntion returning an object, or a simple object for attributes applied 2043 * to the DOM node. Default is empty object. 2044 * @param {Function|String} contentsArg 2045 * A function returning a string, or a simple string for the HTML code inside 2046 * the root DOM node. Default is empty string. 2047 * @example 2048 */ 2049 uiElement : function( dialog, elementDefinition, htmlList, nodeNameArg, stylesArg, attributesArg, contentsArg ) 2050 { 2051 if ( arguments.length < 4 ) 2052 return; 2053 2054 var nodeName = ( nodeNameArg.call ? nodeNameArg( elementDefinition ) : nodeNameArg ) || 'div', 2055 html = [ '<', nodeName, ' ' ], 2056 styles = ( stylesArg && stylesArg.call ? stylesArg( elementDefinition ) : stylesArg ) || {}, 2057 attributes = ( attributesArg && attributesArg.call ? attributesArg( elementDefinition ) : attributesArg ) || {}, 2058 innerHTML = ( contentsArg && contentsArg.call ? contentsArg.call( this, dialog, elementDefinition ) : contentsArg ) || '', 2059 domId = this.domId = attributes.id || CKEDITOR.tools.getNextId() + '_uiElement', 2060 id = this.id = elementDefinition.id, 2061 i; 2062 2063 // Set the id, a unique id is required for getElement() to work. 2064 attributes.id = domId; 2065 2066 // Set the type and definition CSS class names. 2067 var classes = {}; 2068 if ( elementDefinition.type ) 2069 classes[ 'cke_dialog_ui_' + elementDefinition.type ] = 1; 2070 if ( elementDefinition.className ) 2071 classes[ elementDefinition.className ] = 1; 2072 var attributeClasses = ( attributes['class'] && attributes['class'].split ) ? attributes['class'].split( ' ' ) : []; 2073 for ( i = 0 ; i < attributeClasses.length ; i++ ) 2074 { 2075 if ( attributeClasses[i] ) 2076 classes[ attributeClasses[i] ] = 1; 2077 } 2078 var finalClasses = []; 2079 for ( i in classes ) 2080 finalClasses.push( i ); 2081 attributes['class'] = finalClasses.join( ' ' ); 2082 2083 // Set the popup tooltop. 2084 if ( elementDefinition.title ) 2085 attributes.title = elementDefinition.title; 2086 2087 // Write the inline CSS styles. 2088 var styleStr = ( elementDefinition.style || '' ).split( ';' ); 2089 for ( i in styles ) 2090 styleStr.push( i + ':' + styles[i] ); 2091 if ( elementDefinition.hidden ) 2092 styleStr.push( 'display:none' ); 2093 for ( i = styleStr.length - 1 ; i >= 0 ; i-- ) 2094 { 2095 if ( styleStr[i] === '' ) 2096 styleStr.splice( i, 1 ); 2097 } 2098 if ( styleStr.length > 0 ) 2099 attributes.style = ( attributes.style ? ( attributes.style + '; ' ) : '' ) + styleStr.join( '; ' ); 2100 2101 // Write the attributes. 2102 for ( i in attributes ) 2103 html.push( i + '="' + CKEDITOR.tools.htmlEncode( attributes[i] ) + '" '); 2104 2105 // Write the content HTML. 2106 html.push( '>', innerHTML, '</', nodeName, '>' ); 2107 2108 // Add contents to the parent HTML array. 2109 htmlList.push( html.join( '' ) ); 2110 2111 ( this._ || ( this._ = {} ) ).dialog = dialog; 2112 2113 // Override isChanged if it is defined in element definition. 2114 if ( typeof( elementDefinition.isChanged ) == 'boolean' ) 2115 this.isChanged = function(){ return elementDefinition.isChanged; }; 2116 if ( typeof( elementDefinition.isChanged ) == 'function' ) 2117 this.isChanged = elementDefinition.isChanged; 2118 2119 // Add events. 2120 CKEDITOR.event.implementOn( this ); 2121 2122 this.registerEvents( elementDefinition ); 2123 if ( this.accessKeyUp && this.accessKeyDown && elementDefinition.accessKey ) 2124 registerAccessKey( this, dialog, 'CTRL+' + elementDefinition.accessKey ); 2125 2126 var me = this; 2127 dialog.on( 'load', function() 2128 { 2129 if ( me.getInputElement() ) 2130 { 2131 me.getInputElement().on( 'focus', function() 2132 { 2133 dialog._.tabBarMode = false; 2134 dialog._.hasFocus = true; 2135 me.fire( 'focus' ); 2136 }, me ); 2137 } 2138 } ); 2139 2140 // Register the object as a tab focus if it can be included. 2141 if ( this.keyboardFocusable ) 2142 { 2143 this.tabIndex = elementDefinition.tabIndex || 0; 2144 2145 this.focusIndex = dialog._.focusList.push( this ) - 1; 2146 this.on( 'focus', function() 2147 { 2148 dialog._.currentFocusIndex = me.focusIndex; 2149 } ); 2150 } 2151 2152 // Completes this object with everything we have in the 2153 // definition. 2154 CKEDITOR.tools.extend( this, elementDefinition ); 2155 }, 2156 2157 /** 2158 * Horizontal layout box for dialog UI elements, auto-expends to available width of container. 2159 * @constructor 2160 * @extends CKEDITOR.ui.dialog.uiElement 2161 * @param {CKEDITOR.dialog} dialog 2162 * Parent dialog object. 2163 * @param {Array} childObjList 2164 * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this 2165 * container. 2166 * @param {Array} childHtmlList 2167 * Array of HTML code that correspond to the HTML output of all the 2168 * objects in childObjList. 2169 * @param {Array} htmlList 2170 * Array of HTML code that this element will output to. 2171 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 2172 * The element definition. Accepted fields: 2173 * <ul> 2174 * <li><strong>widths</strong> (Optional) The widths of child cells.</li> 2175 * <li><strong>height</strong> (Optional) The height of the layout.</li> 2176 * <li><strong>padding</strong> (Optional) The padding width inside child 2177 * cells.</li> 2178 * <li><strong>align</strong> (Optional) The alignment of the whole layout 2179 * </li> 2180 * </ul> 2181 * @example 2182 */ 2183 hbox : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition ) 2184 { 2185 if ( arguments.length < 4 ) 2186 return; 2187 2188 this._ || ( this._ = {} ); 2189 2190 var children = this._.children = childObjList, 2191 widths = elementDefinition && elementDefinition.widths || null, 2192 height = elementDefinition && elementDefinition.height || null, 2193 styles = {}, 2194 i; 2195 /** @ignore */ 2196 var innerHTML = function() 2197 { 2198 var html = [ '<tbody><tr class="cke_dialog_ui_hbox">' ]; 2199 for ( i = 0 ; i < childHtmlList.length ; i++ ) 2200 { 2201 var className = 'cke_dialog_ui_hbox_child', 2202 styles = []; 2203 if ( i === 0 ) 2204 className = 'cke_dialog_ui_hbox_first'; 2205 if ( i == childHtmlList.length - 1 ) 2206 className = 'cke_dialog_ui_hbox_last'; 2207 html.push( '<td class="', className, '" role="presentation" ' ); 2208 if ( widths ) 2209 { 2210 if ( widths[i] ) 2211 styles.push( 'width:' + CKEDITOR.tools.cssLength( widths[i] ) ); 2212 } 2213 else 2214 styles.push( 'width:' + Math.floor( 100 / childHtmlList.length ) + '%' ); 2215 if ( height ) 2216 styles.push( 'height:' + CKEDITOR.tools.cssLength( height ) ); 2217 if ( elementDefinition && elementDefinition.padding != undefined ) 2218 styles.push( 'padding:' + CKEDITOR.tools.cssLength( elementDefinition.padding ) ); 2219 if ( styles.length > 0 ) 2220 html.push( 'style="' + styles.join('; ') + '" ' ); 2221 html.push( '>', childHtmlList[i], '</td>' ); 2222 } 2223 html.push( '</tr></tbody>' ); 2224 return html.join( '' ); 2225 }; 2226 2227 var attribs = { role : 'presentation' }; 2228 elementDefinition && elementDefinition.align && ( attribs.align = elementDefinition.align ); 2229 2230 CKEDITOR.ui.dialog.uiElement.call( 2231 this, 2232 dialog, 2233 elementDefinition || { type : 'hbox' }, 2234 htmlList, 2235 'table', 2236 styles, 2237 attribs, 2238 innerHTML ); 2239 }, 2240 2241 /** 2242 * Vertical layout box for dialog UI elements. 2243 * @constructor 2244 * @extends CKEDITOR.ui.dialog.hbox 2245 * @param {CKEDITOR.dialog} dialog 2246 * Parent dialog object. 2247 * @param {Array} childObjList 2248 * Array of {@link CKEDITOR.ui.dialog.uiElement} objects inside this 2249 * container. 2250 * @param {Array} childHtmlList 2251 * Array of HTML code that correspond to the HTML output of all the 2252 * objects in childObjList. 2253 * @param {Array} htmlList 2254 * Array of HTML code that this element will output to. 2255 * @param {CKEDITOR.dialog.uiElementDefinition} elementDefinition 2256 * The element definition. Accepted fields: 2257 * <ul> 2258 * <li><strong>width</strong> (Optional) The width of the layout.</li> 2259 * <li><strong>heights</strong> (Optional) The heights of individual cells. 2260 * </li> 2261 * <li><strong>align</strong> (Optional) The alignment of the layout.</li> 2262 * <li><strong>padding</strong> (Optional) The padding width inside child 2263 * cells.</li> 2264 * <li><strong>expand</strong> (Optional) Whether the layout should expand 2265 * vertically to fill its container.</li> 2266 * </ul> 2267 * @example 2268 */ 2269 vbox : function( dialog, childObjList, childHtmlList, htmlList, elementDefinition ) 2270 { 2271 if (arguments.length < 3 ) 2272 return; 2273 2274 this._ || ( this._ = {} ); 2275 2276 var children = this._.children = childObjList, 2277 width = elementDefinition && elementDefinition.width || null, 2278 heights = elementDefinition && elementDefinition.heights || null; 2279 /** @ignore */ 2280 var innerHTML = function() 2281 { 2282 var html = [ '<table role="presentation" cellspacing="0" border="0" ' ]; 2283 html.push( 'style="' ); 2284 if ( elementDefinition && elementDefinition.expand ) 2285 html.push( 'height:100%;' ); 2286 html.push( 'width:' + CKEDITOR.tools.cssLength( width || '100%' ), ';' ); 2287 html.push( '"' ); 2288 html.push( 'align="', CKEDITOR.tools.htmlEncode( 2289 ( elementDefinition && elementDefinition.align ) || ( dialog.getParentEditor().lang.dir == 'ltr' ? 'left' : 'right' ) ), '" ' ); 2290 2291 html.push( '><tbody>' ); 2292 for ( var i = 0 ; i < childHtmlList.length ; i++ ) 2293 { 2294 var styles = []; 2295 html.push( '<tr><td role="presentation" ' ); 2296 if ( width ) 2297 styles.push( 'width:' + CKEDITOR.tools.cssLength( width || '100%' ) ); 2298 if ( heights ) 2299 styles.push( 'height:' + CKEDITOR.tools.cssLength( heights[i] ) ); 2300 else if ( elementDefinition && elementDefinition.expand ) 2301 styles.push( 'height:' + Math.floor( 100 / childHtmlList.length ) + '%' ); 2302 if ( elementDefinition && elementDefinition.padding != undefined ) 2303 styles.push( 'padding:' + CKEDITOR.tools.cssLength( elementDefinition.padding ) ); 2304 if ( styles.length > 0 ) 2305 html.push( 'style="', styles.join( '; ' ), '" ' ); 2306 html.push( ' class="cke_dialog_ui_vbox_child">', childHtmlList[i], '</td></tr>' ); 2307 } 2308 html.push( '</tbody></table>' ); 2309 return html.join( '' ); 2310 }; 2311 CKEDITOR.ui.dialog.uiElement.call( this, dialog, elementDefinition || { type : 'vbox' }, htmlList, 'div', null, { role : 'presentation' }, innerHTML ); 2312 } 2313 }; 2314 })(); 2315 2316 CKEDITOR.ui.dialog.uiElement.prototype = 2317 { 2318 /** 2319 * Gets the root DOM element of this dialog UI object. 2320 * @returns {CKEDITOR.dom.element} Root DOM element of UI object. 2321 * @example 2322 * uiElement.getElement().hide(); 2323 */ 2324 getElement : function() 2325 { 2326 return CKEDITOR.document.getById( this.domId ); 2327 }, 2328 2329 /** 2330 * Gets the DOM element that the user inputs values. 2331 * This function is used by setValue(), getValue() and focus(). It should 2332 * be overrided in child classes where the input element isn't the root 2333 * element. 2334 * @returns {CKEDITOR.dom.element} The element where the user input values. 2335 * @example 2336 * var rawValue = textInput.getInputElement().$.value; 2337 */ 2338 getInputElement : function() 2339 { 2340 return this.getElement(); 2341 }, 2342 2343 /** 2344 * Gets the parent dialog object containing this UI element. 2345 * @returns {CKEDITOR.dialog} Parent dialog object. 2346 * @example 2347 * var dialog = uiElement.getDialog(); 2348 */ 2349 getDialog : function() 2350 { 2351 return this._.dialog; 2352 }, 2353 2354 /** 2355 * Sets the value of this dialog UI object. 2356 * @param {Object} value The new value. 2357 * @param {Boolean} noChangeEvent Internal commit, to supress 'change' event on this element. 2358 * @returns {CKEDITOR.dialog.uiElement} The current UI element. 2359 * @example 2360 * uiElement.setValue( 'Dingo' ); 2361 */ 2362 setValue : function( value, noChangeEvent ) 2363 { 2364 this.getInputElement().setValue( value ); 2365 !noChangeEvent && this.fire( 'change', { value : value } ); 2366 return this; 2367 }, 2368 2369 /** 2370 * Gets the current value of this dialog UI object. 2371 * @returns {Object} The current value. 2372 * @example 2373 * var myValue = uiElement.getValue(); 2374 */ 2375 getValue : function() 2376 { 2377 return this.getInputElement().getValue(); 2378 }, 2379 2380 /** 2381 * Tells whether the UI object's value has changed. 2382 * @returns {Boolean} true if changed, false if not changed. 2383 * @example 2384 * if ( uiElement.isChanged() ) 2385 * confirm( 'Value changed! Continue?' ); 2386 */ 2387 isChanged : function() 2388 { 2389 // Override in input classes. 2390 return false; 2391 }, 2392 2393 /** 2394 * Selects the parent tab of this element. Usually called by focus() or overridden focus() methods. 2395 * @returns {CKEDITOR.dialog.uiElement} The current UI element. 2396 * @example 2397 * focus : function() 2398 * { 2399 * this.selectParentTab(); 2400 * // do something else. 2401 * } 2402 */ 2403 selectParentTab : function() 2404 { 2405 var element = this.getInputElement(), 2406 cursor = element, 2407 tabId; 2408 while ( ( cursor = cursor.getParent() ) && cursor.$.className.search( 'cke_dialog_page_contents' ) == -1 ) 2409 { /*jsl:pass*/ } 2410 2411 // Some widgets don't have parent tabs (e.g. OK and Cancel buttons). 2412 if ( !cursor ) 2413 return this; 2414 2415 tabId = cursor.getAttribute( 'name' ); 2416 // Avoid duplicate select. 2417 if ( this._.dialog._.currentTabId != tabId ) 2418 this._.dialog.selectPage( tabId ); 2419 return this; 2420 }, 2421 2422 /** 2423 * Puts the focus to the UI object. Switches tabs if the UI object isn't in the active tab page. 2424 * @returns {CKEDITOR.dialog.uiElement} The current UI element. 2425 * @example 2426 * uiElement.focus(); 2427 */ 2428 focus : function() 2429 { 2430 this.selectParentTab().getInputElement().focus(); 2431 return this; 2432 }, 2433 2434 /** 2435 * Registers the on* event handlers defined in the element definition. 2436 * The default behavior of this function is: 2437 * <ol> 2438 * <li> 2439 * If the on* event is defined in the class's eventProcesors list, 2440 * then the registration is delegated to the corresponding function 2441 * in the eventProcessors list. 2442 * </li> 2443 * <li> 2444 * If the on* event is not defined in the eventProcessors list, then 2445 * register the event handler under the corresponding DOM event of 2446 * the UI element's input DOM element (as defined by the return value 2447 * of {@link CKEDITOR.ui.dialog.uiElement#getInputElement}). 2448 * </li> 2449 * </ol> 2450 * This function is only called at UI element instantiation, but can 2451 * be overridded in child classes if they require more flexibility. 2452 * @param {CKEDITOR.dialog.uiElementDefinition} definition The UI element 2453 * definition. 2454 * @returns {CKEDITOR.dialog.uiElement} The current UI element. 2455 * @example 2456 */ 2457 registerEvents : function( definition ) 2458 { 2459 var regex = /^on([A-Z]\w+)/, 2460 match; 2461 2462 var registerDomEvent = function( uiElement, dialog, eventName, func ) 2463 { 2464 dialog.on( 'load', function() 2465 { 2466 uiElement.getInputElement().on( eventName, func, uiElement ); 2467 }); 2468 }; 2469 2470 for ( var i in definition ) 2471 { 2472 if ( !( match = i.match( regex ) ) ) 2473 continue; 2474 if ( this.eventProcessors[i] ) 2475 this.eventProcessors[i].call( this, this._.dialog, definition[i] ); 2476 else 2477 registerDomEvent( this, this._.dialog, match[1].toLowerCase(), definition[i] ); 2478 } 2479 2480 return this; 2481 }, 2482 2483 /** 2484 * The event processor list used by 2485 * {@link CKEDITOR.ui.dialog.uiElement#getInputElement} at UI element 2486 * instantiation. The default list defines three on* events: 2487 * <ol> 2488 * <li>onLoad - Called when the element's parent dialog opens for the 2489 * first time</li> 2490 * <li>onShow - Called whenever the element's parent dialog opens.</li> 2491 * <li>onHide - Called whenever the element's parent dialog closes.</li> 2492 * </ol> 2493 * @field 2494 * @type Object 2495 * @example 2496 * // This connects the 'click' event in CKEDITOR.ui.dialog.button to onClick 2497 * // handlers in the UI element's definitions. 2498 * CKEDITOR.ui.dialog.button.eventProcessors = CKEDITOR.tools.extend( {}, 2499 * CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors, 2500 * { onClick : function( dialog, func ) { this.on( 'click', func ); } }, 2501 * true ); 2502 */ 2503 eventProcessors : 2504 { 2505 onLoad : function( dialog, func ) 2506 { 2507 dialog.on( 'load', func, this ); 2508 }, 2509 2510 onShow : function( dialog, func ) 2511 { 2512 dialog.on( 'show', func, this ); 2513 }, 2514 2515 onHide : function( dialog, func ) 2516 { 2517 dialog.on( 'hide', func, this ); 2518 } 2519 }, 2520 2521 /** 2522 * The default handler for a UI element's access key down event, which 2523 * tries to put focus to the UI element.<br /> 2524 * Can be overridded in child classes for more sophisticaed behavior. 2525 * @param {CKEDITOR.dialog} dialog The parent dialog object. 2526 * @param {String} key The key combination pressed. Since access keys 2527 * are defined to always include the CTRL key, its value should always 2528 * include a 'CTRL+' prefix. 2529 * @example 2530 */ 2531 accessKeyDown : function( dialog, key ) 2532 { 2533 this.focus(); 2534 }, 2535 2536 /** 2537 * The default handler for a UI element's access key up event, which 2538 * does nothing.<br /> 2539 * Can be overridded in child classes for more sophisticated behavior. 2540 * @param {CKEDITOR.dialog} dialog The parent dialog object. 2541 * @param {String} key The key combination pressed. Since access keys 2542 * are defined to always include the CTRL key, its value should always 2543 * include a 'CTRL+' prefix. 2544 * @example 2545 */ 2546 accessKeyUp : function( dialog, key ) 2547 { 2548 }, 2549 2550 /** 2551 * Disables a UI element. 2552 * @example 2553 */ 2554 disable : function() 2555 { 2556 var element = this.getInputElement(); 2557 element.setAttribute( 'disabled', 'true' ); 2558 element.addClass( 'cke_disabled' ); 2559 }, 2560 2561 /** 2562 * Enables a UI element. 2563 * @example 2564 */ 2565 enable : function() 2566 { 2567 var element = this.getInputElement(); 2568 element.removeAttribute( 'disabled' ); 2569 element.removeClass( 'cke_disabled' ); 2570 }, 2571 2572 /** 2573 * Determines whether an UI element is enabled or not. 2574 * @returns {Boolean} Whether the UI element is enabled. 2575 * @example 2576 */ 2577 isEnabled : function() 2578 { 2579 return !this.getInputElement().getAttribute( 'disabled' ); 2580 }, 2581 2582 /** 2583 * Determines whether an UI element is visible or not. 2584 * @returns {Boolean} Whether the UI element is visible. 2585 * @example 2586 */ 2587 isVisible : function() 2588 { 2589 return this.getInputElement().isVisible(); 2590 }, 2591 2592 /** 2593 * Determines whether an UI element is focus-able or not. 2594 * Focus-able is defined as being both visible and enabled. 2595 * @returns {Boolean} Whether the UI element can be focused. 2596 * @example 2597 */ 2598 isFocusable : function() 2599 { 2600 if ( !this.isEnabled() || !this.isVisible() ) 2601 return false; 2602 return true; 2603 } 2604 }; 2605 2606 CKEDITOR.ui.dialog.hbox.prototype = CKEDITOR.tools.extend( new CKEDITOR.ui.dialog.uiElement, 2607 /** 2608 * @lends CKEDITOR.ui.dialog.hbox.prototype 2609 */ 2610 { 2611 /** 2612 * Gets a child UI element inside this container. 2613 * @param {Array|Number} indices An array or a single number to indicate the child's 2614 * position in the container's descendant tree. Omit to get all the children in an array. 2615 * @returns {Array|CKEDITOR.ui.dialog.uiElement} Array of all UI elements in the container 2616 * if no argument given, or the specified UI element if indices is given. 2617 * @example 2618 * var checkbox = hbox.getChild( [0,1] ); 2619 * checkbox.setValue( true ); 2620 */ 2621 getChild : function( indices ) 2622 { 2623 // If no arguments, return a clone of the children array. 2624 if ( arguments.length < 1 ) 2625 return this._.children.concat(); 2626 2627 // If indices isn't array, make it one. 2628 if ( !indices.splice ) 2629 indices = [ indices ]; 2630 2631 // Retrieve the child element according to tree position. 2632 if ( indices.length < 2 ) 2633 return this._.children[ indices[0] ]; 2634 else 2635 return ( this._.children[ indices[0] ] && this._.children[ indices[0] ].getChild ) ? 2636 this._.children[ indices[0] ].getChild( indices.slice( 1, indices.length ) ) : 2637 null; 2638 } 2639 }, true ); 2640 2641 CKEDITOR.ui.dialog.vbox.prototype = new CKEDITOR.ui.dialog.hbox(); 2642 2643 2644 2645 (function() 2646 { 2647 var commonBuilder = { 2648 build : function( dialog, elementDefinition, output ) 2649 { 2650 var children = elementDefinition.children, 2651 child, 2652 childHtmlList = [], 2653 childObjList = []; 2654 for ( var i = 0 ; ( i < children.length && ( child = children[i] ) ) ; i++ ) 2655 { 2656 var childHtml = []; 2657 childHtmlList.push( childHtml ); 2658 childObjList.push( CKEDITOR.dialog._.uiElementBuilders[ child.type ].build( dialog, child, childHtml ) ); 2659 } 2660 return new CKEDITOR.ui.dialog[elementDefinition.type]( dialog, childObjList, childHtmlList, output, elementDefinition ); 2661 } 2662 }; 2663 2664 CKEDITOR.dialog.addUIElement( 'hbox', commonBuilder ); 2665 CKEDITOR.dialog.addUIElement( 'vbox', commonBuilder ); 2666 })(); 2667 2668 /** 2669 * Generic dialog command. It opens a specific dialog when executed. 2670 * @constructor 2671 * @augments CKEDITOR.commandDefinition 2672 * @param {string} dialogName The name of the dialog to open when executing 2673 * this command. 2674 * @example 2675 * // Register the "link" command, which opens the "link" dialog. 2676 * editor.addCommand( 'link', <b>new CKEDITOR.dialogCommand( 'link' )</b> ); 2677 */ 2678 CKEDITOR.dialogCommand = function( dialogName ) 2679 { 2680 this.dialogName = dialogName; 2681 }; 2682 2683 CKEDITOR.dialogCommand.prototype = 2684 { 2685 /** @ignore */ 2686 exec : function( editor ) 2687 { 2688 editor.openDialog( this.dialogName ); 2689 }, 2690 2691 // Dialog commands just open a dialog ui, thus require no undo logic, 2692 // undo support should dedicate to specific dialog implementation. 2693 canUndo: false, 2694 2695 editorFocus : CKEDITOR.env.ie || CKEDITOR.env.webkit 2696 }; 2697 2698 (function() 2699 { 2700 var notEmptyRegex = /^([a]|[^a])+$/, 2701 integerRegex = /^\d*$/, 2702 numberRegex = /^\d*(?:\.\d+)?$/; 2703 2704 CKEDITOR.VALIDATE_OR = 1; 2705 CKEDITOR.VALIDATE_AND = 2; 2706 2707 CKEDITOR.dialog.validate = 2708 { 2709 functions : function() 2710 { 2711 return function() 2712 { 2713 /** 2714 * It's important for validate functions to be able to accept the value 2715 * as argument in addition to this.getValue(), so that it is possible to 2716 * combine validate functions together to make more sophisticated 2717 * validators. 2718 */ 2719 var value = this && this.getValue ? this.getValue() : arguments[0]; 2720 2721 var msg = undefined, 2722 relation = CKEDITOR.VALIDATE_AND, 2723 functions = [], i; 2724 2725 for ( i = 0 ; i < arguments.length ; i++ ) 2726 { 2727 if ( typeof( arguments[i] ) == 'function' ) 2728 functions.push( arguments[i] ); 2729 else 2730 break; 2731 } 2732 2733 if ( i < arguments.length && typeof( arguments[i] ) == 'string' ) 2734 { 2735 msg = arguments[i]; 2736 i++; 2737 } 2738 2739 if ( i < arguments.length && typeof( arguments[i]) == 'number' ) 2740 relation = arguments[i]; 2741 2742 var passed = ( relation == CKEDITOR.VALIDATE_AND ? true : false ); 2743 for ( i = 0 ; i < functions.length ; i++ ) 2744 { 2745 if ( relation == CKEDITOR.VALIDATE_AND ) 2746 passed = passed && functions[i]( value ); 2747 else 2748 passed = passed || functions[i]( value ); 2749 } 2750 2751 if ( !passed ) 2752 { 2753 if ( msg !== undefined ) 2754 alert( msg ); 2755 if ( this && ( this.select || this.focus ) ) 2756 ( this.select || this.focus )(); 2757 return false; 2758 } 2759 2760 return true; 2761 }; 2762 }, 2763 2764 regex : function( regex, msg ) 2765 { 2766 /* 2767 * Can be greatly shortened by deriving from functions validator if code size 2768 * turns out to be more important than performance. 2769 */ 2770 return function() 2771 { 2772 var value = this && this.getValue ? this.getValue() : arguments[0]; 2773 if ( !regex.test( value ) ) 2774 { 2775 if ( msg !== undefined ) 2776 alert( msg ); 2777 if ( this && ( this.select || this.focus ) ) 2778 { 2779 if ( this.select ) 2780 this.select(); 2781 else 2782 this.focus(); 2783 } 2784 return false; 2785 } 2786 return true; 2787 }; 2788 }, 2789 2790 notEmpty : function( msg ) 2791 { 2792 return this.regex( notEmptyRegex, msg ); 2793 }, 2794 2795 integer : function( msg ) 2796 { 2797 return this.regex( integerRegex, msg ); 2798 }, 2799 2800 'number' : function( msg ) 2801 { 2802 return this.regex( numberRegex, msg ); 2803 }, 2804 2805 equals : function( value, msg ) 2806 { 2807 return this.functions( function( val ){ return val == value; }, msg ); 2808 }, 2809 2810 notEqual : function( value, msg ) 2811 { 2812 return this.functions( function( val ){ return val != value; }, msg ); 2813 } 2814 }; 2815 2816 CKEDITOR.on( 'instanceDestroyed', function( evt ) 2817 { 2818 // Remove dialog cover on last instance destroy. 2819 if ( CKEDITOR.tools.isEmpty( CKEDITOR.instances ) ) 2820 { 2821 var currentTopDialog; 2822 while ( ( currentTopDialog = CKEDITOR.dialog._.currentTop ) ) 2823 currentTopDialog.hide(); 2824 removeCovers(); 2825 } 2826 2827 var dialogs = evt.editor._.storedDialogs; 2828 for ( var name in dialogs ) 2829 dialogs[ name ].destroy(); 2830 2831 }); 2832 2833 })(); 2834 })(); 2835 2836 // Extend the CKEDITOR.editor class with dialog specific functions. 2837 CKEDITOR.tools.extend( CKEDITOR.editor.prototype, 2838 /** @lends CKEDITOR.editor.prototype */ 2839 { 2840 /** 2841 * Loads and opens a registered dialog. 2842 * @param {String} dialogName The registered name of the dialog. 2843 * @param {Function} callback The function to be invoked after dialog instance created. 2844 * @see CKEDITOR.dialog.add 2845 * @example 2846 * CKEDITOR.instances.editor1.openDialog( 'smiley' ); 2847 * @returns {CKEDITOR.dialog} The dialog object corresponding to the dialog displayed. null if the dialog name is not registered. 2848 */ 2849 openDialog : function( dialogName, callback ) 2850 { 2851 var dialogDefinitions = CKEDITOR.dialog._.dialogDefinitions[ dialogName ], 2852 dialogSkin = this.skin.dialog; 2853 2854 // If the dialogDefinition is already loaded, open it immediately. 2855 if ( typeof dialogDefinitions == 'function' && dialogSkin._isLoaded ) 2856 { 2857 var storedDialogs = this._.storedDialogs || 2858 ( this._.storedDialogs = {} ); 2859 2860 var dialog = storedDialogs[ dialogName ] || 2861 ( storedDialogs[ dialogName ] = new CKEDITOR.dialog( this, dialogName ) ); 2862 2863 callback && callback.call( dialog, dialog ); 2864 dialog.show(); 2865 2866 return dialog; 2867 } 2868 else if ( dialogDefinitions == 'failed' ) 2869 throw new Error( '[CKEDITOR.dialog.openDialog] Dialog "' + dialogName + '" failed when loading definition.' ); 2870 2871 // Not loaded? Load the .js file first. 2872 var body = CKEDITOR.document.getBody(), 2873 cursor = body.$.style.cursor, 2874 me = this; 2875 2876 body.setStyle( 'cursor', 'wait' ); 2877 2878 function onDialogFileLoaded( success ) 2879 { 2880 var dialogDefinition = CKEDITOR.dialog._.dialogDefinitions[ dialogName ], 2881 skin = me.skin.dialog; 2882 2883 // Check if both skin part and definition is loaded. 2884 if ( !skin._isLoaded || loadDefinition && typeof success == 'undefined' ) 2885 return; 2886 2887 // In case of plugin error, mark it as loading failed. 2888 if ( typeof dialogDefinition != 'function' ) 2889 CKEDITOR.dialog._.dialogDefinitions[ dialogName ] = 'failed'; 2890 2891 me.openDialog( dialogName, callback ); 2892 body.setStyle( 'cursor', cursor ); 2893 } 2894 2895 if ( typeof dialogDefinitions == 'string' ) 2896 { 2897 var loadDefinition = 1; 2898 CKEDITOR.scriptLoader.load( CKEDITOR.getUrl( dialogDefinitions ), onDialogFileLoaded ); 2899 } 2900 2901 CKEDITOR.skins.load( this, 'dialog', onDialogFileLoaded ); 2902 2903 return null; 2904 } 2905 }); 2906 2907 CKEDITOR.plugins.add( 'dialog', 2908 { 2909 requires : [ 'dialogui' ] 2910 }); 2911 2912 // Dialog related configurations. 2913 2914 /** 2915 * The color of the dialog background cover. It should be a valid CSS color 2916 * string. 2917 * @name CKEDITOR.config.dialog_backgroundCoverColor 2918 * @type String 2919 * @default 'white' 2920 * @example 2921 * config.dialog_backgroundCoverColor = 'rgb(255, 254, 253)'; 2922 */ 2923 2924 /** 2925 * The opacity of the dialog background cover. It should be a number within the 2926 * range [0.0, 1.0]. 2927 * @name CKEDITOR.config.dialog_backgroundCoverOpacity 2928 * @type Number 2929 * @default 0.5 2930 * @example 2931 * config.dialog_backgroundCoverOpacity = 0.7; 2932 */ 2933 2934 /** 2935 * If the dialog has more than one tab, put focus into the first tab as soon as dialog is opened. 2936 * @name CKEDITOR.config.dialog_startupFocusTab 2937 * @type Boolean 2938 * @default false 2939 * @example 2940 * config.dialog_startupFocusTab = true; 2941 */ 2942 2943 /** 2944 * The distance of magnetic borders used in moving and resizing dialogs, 2945 * measured in pixels. 2946 * @name CKEDITOR.config.dialog_magnetDistance 2947 * @type Number 2948 * @default 20 2949 * @example 2950 * config.dialog_magnetDistance = 30; 2951 */ 2952 2953 /** 2954 * Fired when a dialog definition is about to be used to create a dialog into 2955 * an editor instance. This event makes it possible to customize the definition 2956 * before creating it. 2957 * <p>Note that this event is called only the first time a specific dialog is 2958 * opened. Successive openings will use the cached dialog, and this event will 2959 * not get fired.</p> 2960 * @name CKEDITOR#dialogDefinition 2961 * @event 2962 * @param {CKEDITOR.dialog.dialogDefinition} data The dialog defination that 2963 * is being loaded. 2964 * @param {CKEDITOR.editor} editor The editor instance that will use the 2965 * dialog. 2966 */ 2967 2968 /** 2969 * Fired when a tab is going to be selected in a dialog 2970 * @name dialog#selectPage 2971 * @event 2972 * @param String page The id of the page that it's gonna be selected. 2973 * @param String currentPage The id of the current page. 2974 */
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |