[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/libraries/ckeditor/_source/plugins/dialog/ -> plugin.js (source)

   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 &lt;span&gt; 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           * &nbsp;&nbsp;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           * &nbsp;&nbsp;CKEDITOR.ui.dialog.uiElement.prototype.eventProcessors,

2500           * &nbsp;&nbsp;{ onClick : function( dialog, func ) { this.on( 'click', func ); } },

2501           * &nbsp;&nbsp;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   */


Generated: Thu Mar 24 11:18:33 2011 Cross-referenced by PHPXref 0.7