[ Index ]

PHP Cross Reference of Drupal 6 (gatewave)

title

Body

[close]

/sites/all/libraries/ckeditor/_source/plugins/list/ -> 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   * @file Insert and remove numbered and bulleted lists.

   8   */
   9  
  10  (function()
  11  {
  12      var listNodeNames = { ol : 1, ul : 1 },
  13          emptyTextRegex = /^[\n\r\t ]*$/;
  14  
  15      CKEDITOR.plugins.list = {
  16          /*

  17           * Convert a DOM list tree into a data structure that is easier to

  18           * manipulate. This operation should be non-intrusive in the sense that it

  19           * does not change the DOM tree, with the exception that it may add some

  20           * markers to the list item nodes when database is specified.

  21           */
  22          listToArray : function( listNode, database, baseArray, baseIndentLevel, grandparentNode )
  23          {
  24              if ( !listNodeNames[ listNode.getName() ] )
  25                  return [];
  26  
  27              if ( !baseIndentLevel )
  28                  baseIndentLevel = 0;
  29              if ( !baseArray )
  30                  baseArray = [];
  31  
  32              // Iterate over all list items to and look for inner lists.

  33              for ( var i = 0, count = listNode.getChildCount() ; i < count ; i++ )
  34              {
  35                  var listItem = listNode.getChild( i );
  36  
  37                  // It may be a text node or some funny stuff.

  38                  if ( listItem.$.nodeName.toLowerCase() != 'li' )
  39                      continue;
  40  
  41                  var itemObj = { 'parent' : listNode, indent : baseIndentLevel, element : listItem, contents : [] };
  42                  if ( !grandparentNode )
  43                  {
  44                      itemObj.grandparent = listNode.getParent();
  45                      if ( itemObj.grandparent && itemObj.grandparent.$.nodeName.toLowerCase() == 'li' )
  46                          itemObj.grandparent = itemObj.grandparent.getParent();
  47                  }
  48                  else
  49                      itemObj.grandparent = grandparentNode;
  50  
  51                  if ( database )
  52                      CKEDITOR.dom.element.setMarker( database, listItem, 'listarray_index', baseArray.length );
  53                  baseArray.push( itemObj );
  54  
  55                  for ( var j = 0, itemChildCount = listItem.getChildCount(), child; j < itemChildCount ; j++ )
  56                  {
  57                      child = listItem.getChild( j );
  58                      if ( child.type == CKEDITOR.NODE_ELEMENT && listNodeNames[ child.getName() ] )
  59                          // Note the recursion here, it pushes inner list items with

  60                          // +1 indentation in the correct order.

  61                          CKEDITOR.plugins.list.listToArray( child, database, baseArray, baseIndentLevel + 1, itemObj.grandparent );
  62                      else
  63                          itemObj.contents.push( child );
  64                  }
  65              }
  66              return baseArray;
  67          },
  68  
  69          // Convert our internal representation of a list back to a DOM forest.

  70          arrayToList : function( listArray, database, baseIndex, paragraphMode, dir )
  71          {
  72              if ( !baseIndex )
  73                  baseIndex = 0;
  74              if ( !listArray || listArray.length < baseIndex + 1 )
  75                  return null;
  76              var doc = listArray[ baseIndex ].parent.getDocument(),
  77                  retval = new CKEDITOR.dom.documentFragment( doc ),
  78                  rootNode = null,
  79                  currentIndex = baseIndex,
  80                  indentLevel = Math.max( listArray[ baseIndex ].indent, 0 ),
  81                  currentListItem = null,
  82                  paragraphName = ( paragraphMode == CKEDITOR.ENTER_P ? 'p' : 'div' );
  83              while ( true )
  84              {
  85                  var item = listArray[ currentIndex ];
  86                  if ( item.indent == indentLevel )
  87                  {
  88                      if ( !rootNode || listArray[ currentIndex ].parent.getName() != rootNode.getName() )
  89                      {
  90                          rootNode = listArray[ currentIndex ].parent.clone( false, true );
  91                          retval.append( rootNode );
  92                      }
  93                      currentListItem = rootNode.append( item.element.clone( false, true ) );
  94                      for ( var i = 0 ; i < item.contents.length ; i++ )
  95                          currentListItem.append( item.contents[i].clone( true, true ) );
  96                      currentIndex++;
  97                  }
  98                  else if ( item.indent == Math.max( indentLevel, 0 ) + 1 )
  99                  {
 100                      var listData = CKEDITOR.plugins.list.arrayToList( listArray, null, currentIndex, paragraphMode );
 101                      currentListItem.append( listData.listNode );
 102                      currentIndex = listData.nextIndex;
 103                  }
 104                  else if ( item.indent == -1 && !baseIndex && item.grandparent )
 105                  {
 106                      currentListItem;
 107                      if ( listNodeNames[ item.grandparent.getName() ] )
 108                          currentListItem = item.element.clone( false, true );
 109                      else
 110                      {
 111                          // Create completely new blocks here, attributes are dropped.

 112                          if ( dir || ( paragraphMode != CKEDITOR.ENTER_BR && item.grandparent.getName() != 'td' ) )
 113                          {
 114                              currentListItem = doc.createElement( paragraphName );
 115                              if ( dir )
 116                                  currentListItem.setAttribute( 'dir', dir );
 117                          }
 118                          else
 119                              currentListItem = new CKEDITOR.dom.documentFragment( doc );
 120                      }
 121  
 122                      for ( i = 0 ; i < item.contents.length ; i++ )
 123                          currentListItem.append( item.contents[i].clone( true, true ) );
 124  
 125                      if ( currentListItem.type == CKEDITOR.NODE_DOCUMENT_FRAGMENT
 126                           && currentIndex != listArray.length - 1 )
 127                      {
 128                          if ( currentListItem.getLast()
 129                                  && currentListItem.getLast().type == CKEDITOR.NODE_ELEMENT
 130                                  && currentListItem.getLast().getAttribute( 'type' ) == '_moz' )
 131                              currentListItem.getLast().remove();
 132                          currentListItem.appendBogus();
 133                      }
 134  
 135                      if ( currentListItem.type == CKEDITOR.NODE_ELEMENT &&
 136                              currentListItem.getName() == paragraphName &&
 137                              currentListItem.$.firstChild )
 138                      {
 139                          currentListItem.trim();
 140                          var firstChild = currentListItem.getFirst();
 141                          if ( firstChild.type == CKEDITOR.NODE_ELEMENT && firstChild.isBlockBoundary() )
 142                          {
 143                              var tmp = new CKEDITOR.dom.documentFragment( doc );
 144                              currentListItem.moveChildren( tmp );
 145                              currentListItem = tmp;
 146                          }
 147                      }
 148  
 149                      var currentListItemName = currentListItem.$.nodeName.toLowerCase();
 150                      if ( !CKEDITOR.env.ie && ( currentListItemName == 'div' || currentListItemName == 'p' ) )
 151                          currentListItem.appendBogus();
 152                      retval.append( currentListItem );
 153                      rootNode = null;
 154                      currentIndex++;
 155                  }
 156                  else
 157                      return null;
 158  
 159                  if ( listArray.length <= currentIndex || Math.max( listArray[ currentIndex ].indent, 0 ) < indentLevel )
 160                      break;
 161              }
 162  
 163              // Clear marker attributes for the new list tree made of cloned nodes, if any.

 164              if ( database )
 165              {
 166                  var currentNode = retval.getFirst();
 167                  while ( currentNode )
 168                  {
 169                      if ( currentNode.type == CKEDITOR.NODE_ELEMENT )
 170                          CKEDITOR.dom.element.clearMarkers( database, currentNode );
 171                      currentNode = currentNode.getNextSourceNode();
 172                  }
 173              }
 174  
 175              return { listNode : retval, nextIndex : currentIndex };
 176          }
 177      };
 178  
 179  	function setState( editor, state )
 180      {
 181          editor.getCommand( this.name ).setState( state );
 182      }
 183  
 184  	function onSelectionChange( evt )
 185      {
 186          var path = evt.data.path,
 187              blockLimit = path.blockLimit,
 188              elements = path.elements,
 189              element;
 190  
 191          // Grouping should only happen under blockLimit.(#3940).

 192          for ( var i = 0 ; i < elements.length && ( element = elements[ i ] )
 193                && !element.equals( blockLimit ); i++ )
 194          {
 195              if ( listNodeNames[ elements[i].getName() ] )
 196              {
 197                  return setState.call( this, evt.editor,
 198                          this.type == elements[i].getName() ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );
 199              }
 200          }
 201  
 202          return setState.call( this, evt.editor, CKEDITOR.TRISTATE_OFF );
 203      }
 204  
 205  	function changeListType( editor, groupObj, database, listsCreated )
 206      {
 207          // This case is easy...

 208          // 1. Convert the whole list into a one-dimensional array.

 209          // 2. Change the list type by modifying the array.

 210          // 3. Recreate the whole list by converting the array to a list.

 211          // 4. Replace the original list with the recreated list.

 212          var listArray = CKEDITOR.plugins.list.listToArray( groupObj.root, database ),
 213              selectedListItems = [];
 214  
 215          for ( var i = 0 ; i < groupObj.contents.length ; i++ )
 216          {
 217              var itemNode = groupObj.contents[i];
 218              itemNode = itemNode.getAscendant( 'li', true );
 219              if ( !itemNode || itemNode.getCustomData( 'list_item_processed' ) )
 220                  continue;
 221              selectedListItems.push( itemNode );
 222              CKEDITOR.dom.element.setMarker( database, itemNode, 'list_item_processed', true );
 223          }
 224  
 225          var root = groupObj.root,
 226              fakeParent = root.getDocument().createElement( this.type );
 227          // Copy all attributes, except from 'start' and 'type'.

 228          root.copyAttributes( fakeParent, { start : 1, type : 1 } );
 229          // The list-style-type property should be ignored.

 230          fakeParent.removeStyle( 'list-style-type' );
 231  
 232          for ( i = 0 ; i < selectedListItems.length ; i++ )
 233          {
 234              var listIndex = selectedListItems[i].getCustomData( 'listarray_index' );
 235              listArray[listIndex].parent = fakeParent;
 236          }
 237          var newList = CKEDITOR.plugins.list.arrayToList( listArray, database, null, editor.config.enterMode );
 238          var child, length = newList.listNode.getChildCount();
 239          for ( i = 0 ; i < length && ( child = newList.listNode.getChild( i ) ) ; i++ )
 240          {
 241              if ( child.getName() == this.type )
 242                  listsCreated.push( child );
 243          }
 244          newList.listNode.replace( groupObj.root );
 245      }
 246  
 247      var headerTagRegex = /^h[1-6]$/;
 248  
 249  	function createList( editor, groupObj, listsCreated )
 250      {
 251          var contents = groupObj.contents,
 252              doc = groupObj.root.getDocument(),
 253              listContents = [];
 254  
 255          // It is possible to have the contents returned by DomRangeIterator to be the same as the root.

 256          // e.g. when we're running into table cells.

 257          // In such a case, enclose the childNodes of contents[0] into a <div>.

 258          if ( contents.length == 1 && contents[0].equals( groupObj.root ) )
 259          {
 260              var divBlock = doc.createElement( 'div' );
 261              contents[0].moveChildren && contents[0].moveChildren( divBlock );
 262              contents[0].append( divBlock );
 263              contents[0] = divBlock;
 264          }
 265  
 266          // Calculate the common parent node of all content blocks.

 267          var commonParent = groupObj.contents[0].getParent();
 268          for ( var i = 0 ; i < contents.length ; i++ )
 269              commonParent = commonParent.getCommonAncestor( contents[i].getParent() );
 270  
 271          // We want to insert things that are in the same tree level only, so calculate the contents again

 272          // by expanding the selected blocks to the same tree level.

 273          for ( i = 0 ; i < contents.length ; i++ )
 274          {
 275              var contentNode = contents[i],
 276                  parentNode;
 277              while ( ( parentNode = contentNode.getParent() ) )
 278              {
 279                  if ( parentNode.equals( commonParent ) )
 280                  {
 281                      listContents.push( contentNode );
 282                      break;
 283                  }
 284                  contentNode = parentNode;
 285              }
 286          }
 287  
 288          if ( listContents.length < 1 )
 289              return;
 290  
 291          // Insert the list to the DOM tree.

 292          var insertAnchor = listContents[ listContents.length - 1 ].getNext(),
 293              listNode = doc.createElement( this.type ),
 294              dir;
 295  
 296          listsCreated.push( listNode );
 297          while ( listContents.length )
 298          {
 299              var contentBlock = listContents.shift(),
 300                  listItem = doc.createElement( 'li' );
 301  
 302              // Preserve heading structure when converting to list item. (#5271)

 303              if ( headerTagRegex.test( contentBlock.getName() ) )
 304                  contentBlock.appendTo( listItem );
 305              else
 306              {
 307                  if ( contentBlock.hasAttribute( 'dir' ) )
 308                  {
 309                      dir = dir || contentBlock.getAttribute( 'dir' );
 310                      contentBlock.removeAttribute( 'dir' );
 311                  }
 312                  contentBlock.copyAttributes( listItem );
 313                  contentBlock.moveChildren( listItem );
 314                  contentBlock.remove();
 315              }
 316  
 317              listItem.appendTo( listNode );
 318  
 319              // Append a bogus BR to force the LI to render at full height

 320              if ( !CKEDITOR.env.ie )
 321                  listItem.appendBogus();
 322          }
 323  
 324          if ( dir )
 325              listNode.setAttribute( 'dir', dir );
 326  
 327          if ( insertAnchor )
 328              listNode.insertBefore( insertAnchor );
 329          else
 330              listNode.appendTo( commonParent );
 331      }
 332  
 333  	function removeList( editor, groupObj, database )
 334      {
 335          // This is very much like the change list type operation.

 336          // Except that we're changing the selected items' indent to -1 in the list array.

 337          var listArray = CKEDITOR.plugins.list.listToArray( groupObj.root, database ),
 338              selectedListItems = [];
 339  
 340          for ( var i = 0 ; i < groupObj.contents.length ; i++ )
 341          {
 342              var itemNode = groupObj.contents[i];
 343              itemNode = itemNode.getAscendant( 'li', true );
 344              if ( !itemNode || itemNode.getCustomData( 'list_item_processed' ) )
 345                  continue;
 346              selectedListItems.push( itemNode );
 347              CKEDITOR.dom.element.setMarker( database, itemNode, 'list_item_processed', true );
 348          }
 349  
 350          var lastListIndex = null;
 351          for ( i = 0 ; i < selectedListItems.length ; i++ )
 352          {
 353              var listIndex = selectedListItems[i].getCustomData( 'listarray_index' );
 354              listArray[listIndex].indent = -1;
 355              lastListIndex = listIndex;
 356          }
 357  
 358          // After cutting parts of the list out with indent=-1, we still have to maintain the array list

 359          // model's nextItem.indent <= currentItem.indent + 1 invariant. Otherwise the array model of the

 360          // list cannot be converted back to a real DOM list.

 361          for ( i = lastListIndex + 1 ; i < listArray.length ; i++ )
 362          {
 363              if ( listArray[i].indent > listArray[i-1].indent + 1 )
 364              {
 365                  var indentOffset = listArray[i-1].indent + 1 - listArray[i].indent;
 366                  var oldIndent = listArray[i].indent;
 367                  while ( listArray[i] && listArray[i].indent >= oldIndent )
 368                  {
 369                      listArray[i].indent += indentOffset;
 370                      i++;
 371                  }
 372                  i--;
 373              }
 374          }
 375  
 376          var newList = CKEDITOR.plugins.list.arrayToList( listArray, database, null, editor.config.enterMode,
 377              groupObj.root.getAttribute( 'dir' ) );
 378  
 379          // Compensate <br> before/after the list node if the surrounds are non-blocks.(#3836)

 380          var docFragment = newList.listNode, boundaryNode, siblingNode;
 381  		function compensateBrs( isStart )
 382          {
 383              if ( ( boundaryNode = docFragment[ isStart ? 'getFirst' : 'getLast' ]() )
 384                   && !( boundaryNode.is && boundaryNode.isBlockBoundary() )
 385                   && ( siblingNode = groupObj.root[ isStart ? 'getPrevious' : 'getNext' ]
 386                        ( CKEDITOR.dom.walker.whitespaces( true ) ) )
 387                   && !( siblingNode.is && siblingNode.isBlockBoundary( { br : 1 } ) ) )
 388                  editor.document.createElement( 'br' )[ isStart ? 'insertBefore' : 'insertAfter' ]( boundaryNode );
 389          }
 390          compensateBrs( true );
 391          compensateBrs();
 392  
 393          docFragment.replace( groupObj.root );
 394      }
 395  
 396  	function listCommand( name, type )
 397      {
 398          this.name = name;
 399          this.type = type;
 400      }
 401  
 402      listCommand.prototype = {
 403          exec : function( editor )
 404          {
 405              editor.focus();
 406  
 407              var doc = editor.document,
 408                  selection = editor.getSelection(),
 409                  ranges = selection && selection.getRanges( true );
 410  
 411              // There should be at least one selected range.

 412              if ( !ranges || ranges.length < 1 )
 413                  return;
 414  
 415              // Midas lists rule #1 says we can create a list even in an empty document.

 416              // But DOM iterator wouldn't run if the document is really empty.

 417              // So create a paragraph if the document is empty and we're going to create a list.

 418              if ( this.state == CKEDITOR.TRISTATE_OFF )
 419              {
 420                  var body = doc.getBody();
 421                  body.trim();
 422                  if ( !body.getFirst() )
 423                  {
 424                      var paragraph = doc.createElement( editor.config.enterMode == CKEDITOR.ENTER_P ? 'p' :
 425                              ( editor.config.enterMode == CKEDITOR.ENTER_DIV ? 'div' : 'br' ) );
 426                      paragraph.appendTo( body );
 427                      ranges = [ new CKEDITOR.dom.range( doc ) ];
 428                      // IE exception on inserting anything when anchor inside <br>.

 429                      if ( paragraph.is( 'br' ) )
 430                      {
 431                          ranges[ 0 ].setStartBefore( paragraph );
 432                          ranges[ 0 ].setEndAfter( paragraph );
 433                      }
 434                      else
 435                          ranges[ 0 ].selectNodeContents( paragraph );
 436                      selection.selectRanges( ranges );
 437                  }
 438                  // Maybe a single range there enclosing the whole list,

 439                  // turn on the list state manually(#4129).

 440                  else
 441                  {
 442                      var range = ranges.length == 1 && ranges[ 0 ],
 443                          enclosedNode = range && range.getEnclosedNode();
 444                      if ( enclosedNode && enclosedNode.is
 445                          && this.type == enclosedNode.getName() )
 446                      {
 447                          setState.call( this, editor, CKEDITOR.TRISTATE_ON );
 448                      }
 449                  }
 450              }
 451  
 452              var bookmarks = selection.createBookmarks( true );
 453  
 454              // Group the blocks up because there are many cases where multiple lists have to be created,

 455              // or multiple lists have to be cancelled.

 456              var listGroups = [],
 457                  database = {},
 458                  rangeIterator = ranges.createIterator(),
 459                  index = 0;
 460  
 461              while ( ( range = rangeIterator.getNextRange() ) && ++index )
 462              {
 463                  var boundaryNodes = range.getBoundaryNodes(),
 464                      startNode = boundaryNodes.startNode,
 465                      endNode = boundaryNodes.endNode;
 466  
 467                  if ( startNode.type == CKEDITOR.NODE_ELEMENT && startNode.getName() == 'td' )
 468                      range.setStartAt( boundaryNodes.startNode, CKEDITOR.POSITION_AFTER_START );
 469  
 470                  if ( endNode.type == CKEDITOR.NODE_ELEMENT && endNode.getName() == 'td' )
 471                      range.setEndAt( boundaryNodes.endNode, CKEDITOR.POSITION_BEFORE_END );
 472  
 473                  var iterator = range.createIterator(),
 474                      block;
 475  
 476                  iterator.forceBrBreak = ( this.state == CKEDITOR.TRISTATE_OFF );
 477  
 478                  while ( ( block = iterator.getNextParagraph() ) )
 479                  {
 480                      // Avoid duplicate blocks get processed across ranges.

 481                      if( block.getCustomData( 'list_block' ) )
 482                          continue;
 483                      else
 484                          CKEDITOR.dom.element.setMarker( database, block, 'list_block', 1 );
 485  
 486                      var path = new CKEDITOR.dom.elementPath( block ),
 487                          pathElements = path.elements,
 488                          pathElementsCount = pathElements.length,
 489                          listNode = null,
 490                          processedFlag = false,
 491                          blockLimit = path.blockLimit,
 492                          element;
 493  
 494                      // First, try to group by a list ancestor.

 495                      for ( var i = pathElementsCount - 1; i >= 0 && ( element = pathElements[ i ] ); i-- )
 496                      {
 497                          if ( listNodeNames[ element.getName() ]
 498                               && blockLimit.contains( element ) )     // Don't leak outside block limit (#3940).
 499                          {
 500                              // If we've encountered a list inside a block limit

 501                              // The last group object of the block limit element should

 502                              // no longer be valid. Since paragraphs after the list

 503                              // should belong to a different group of paragraphs before

 504                              // the list. (Bug #1309)

 505                              blockLimit.removeCustomData( 'list_group_object_' + index );
 506  
 507                              var groupObj = element.getCustomData( 'list_group_object' );
 508                              if ( groupObj )
 509                                  groupObj.contents.push( block );
 510                              else
 511                              {
 512                                  groupObj = { root : element, contents : [ block ] };
 513                                  listGroups.push( groupObj );
 514                                  CKEDITOR.dom.element.setMarker( database, element, 'list_group_object', groupObj );
 515                              }
 516                              processedFlag = true;
 517                              break;
 518                          }
 519                      }
 520  
 521                      if ( processedFlag )
 522                          continue;
 523  
 524                      // No list ancestor? Group by block limit, but don't mix contents from different ranges.

 525                      var root = blockLimit;
 526                      if ( root.getCustomData( 'list_group_object_' + index ) )
 527                          root.getCustomData( 'list_group_object_' + index ).contents.push( block );
 528                      else
 529                      {
 530                          groupObj = { root : root, contents : [ block ] };
 531                          CKEDITOR.dom.element.setMarker( database, root, 'list_group_object_' + index, groupObj );
 532                          listGroups.push( groupObj );
 533                      }
 534                  }
 535              }
 536  
 537              // Now we have two kinds of list groups, groups rooted at a list, and groups rooted at a block limit element.

 538              // We either have to build lists or remove lists, for removing a list does not makes sense when we are looking

 539              // at the group that's not rooted at lists. So we have three cases to handle.

 540              var listsCreated = [];
 541              while ( listGroups.length > 0 )
 542              {
 543                  groupObj = listGroups.shift();
 544                  if ( this.state == CKEDITOR.TRISTATE_OFF )
 545                  {
 546                      if ( listNodeNames[ groupObj.root.getName() ] )
 547                          changeListType.call( this, editor, groupObj, database, listsCreated );
 548                      else
 549                          createList.call( this, editor, groupObj, listsCreated );
 550                  }
 551                  else if ( this.state == CKEDITOR.TRISTATE_ON && listNodeNames[ groupObj.root.getName() ] )
 552                      removeList.call( this, editor, groupObj, database );
 553              }
 554  
 555              // For all new lists created, merge adjacent, same type lists.

 556              for ( i = 0 ; i < listsCreated.length ; i++ )
 557              {
 558                  listNode = listsCreated[i];
 559                  var mergeSibling, listCommand = this;
 560                  ( mergeSibling = function( rtl ){
 561  
 562                      var sibling = listNode[ rtl ?
 563                          'getPrevious' : 'getNext' ]( CKEDITOR.dom.walker.whitespaces( true ) );
 564                      if ( sibling && sibling.getName &&
 565                           sibling.getName() == listCommand.type )
 566                      {
 567                          sibling.remove();
 568                          // Move children order by merge direction.(#3820)

 569                          sibling.moveChildren( listNode, rtl ? true : false );
 570                      }
 571                  } )();
 572                  mergeSibling( true );
 573              }
 574  
 575              // Clean up, restore selection and update toolbar button states.

 576              CKEDITOR.dom.element.clearAllMarkers( database );
 577              selection.selectBookmarks( bookmarks );
 578              editor.focus();
 579          }
 580      };
 581  
 582      var dtd = CKEDITOR.dtd;
 583      var tailNbspRegex = /[\t\r\n ]*(?:&nbsp;|\xa0)$/;
 584  
 585  	function indexOfFirstChildElement( element, tagNameList )
 586      {
 587          var child,
 588              children = element.children,
 589              length = children.length;
 590  
 591          for ( var i = 0 ; i < length ; i++ )
 592          {
 593              child = children[ i ];
 594              if ( child.name && ( child.name in tagNameList ) )
 595                  return i;
 596          }
 597  
 598          return length;
 599      }
 600  
 601  	function getExtendNestedListFilter( isHtmlFilter )
 602      {
 603          // An element filter function that corrects nested list start in an empty

 604          // list item for better displaying/outputting. (#3165)

 605          return function( listItem )
 606          {
 607              var children = listItem.children,
 608                  firstNestedListIndex = indexOfFirstChildElement( listItem, dtd.$list ),
 609                  firstNestedList = children[ firstNestedListIndex ],
 610                  nodeBefore = firstNestedList && firstNestedList.previous,
 611                  tailNbspmatch;
 612  
 613              if ( nodeBefore
 614                  && ( nodeBefore.name && nodeBefore.name == 'br'
 615                      || nodeBefore.value && ( tailNbspmatch = nodeBefore.value.match( tailNbspRegex ) ) ) )
 616              {
 617                  var fillerNode = nodeBefore;
 618  
 619                  // Always use 'nbsp' as filler node if we found a nested list appear

 620                  // in front of a list item.

 621                  if ( !( tailNbspmatch && tailNbspmatch.index ) && fillerNode == children[ 0 ] )
 622                      children[ 0 ] = ( isHtmlFilter || CKEDITOR.env.ie ) ?
 623                                       new CKEDITOR.htmlParser.text( '\xa0' ) :
 624                                       new CKEDITOR.htmlParser.element( 'br', {} );
 625  
 626                  // Otherwise the filler is not needed anymore.

 627                  else if ( fillerNode.name == 'br' )
 628                      children.splice( firstNestedListIndex - 1, 1 );
 629                  else
 630                      fillerNode.value = fillerNode.value.replace( tailNbspRegex, '' );
 631              }
 632  
 633          };
 634      }
 635  
 636      var defaultListDataFilterRules = { elements : {} };
 637      for ( var i in dtd.$listItem )
 638          defaultListDataFilterRules.elements[ i ] = getExtendNestedListFilter();
 639  
 640      var defaultListHtmlFilterRules = { elements : {} };
 641      for ( i in dtd.$listItem )
 642          defaultListHtmlFilterRules.elements[ i ] = getExtendNestedListFilter( true );
 643  
 644      CKEDITOR.plugins.add( 'list',
 645      {
 646          init : function( editor )
 647          {
 648              // Register commands.

 649              var numberedListCommand = new listCommand( 'numberedlist', 'ol' ),
 650                  bulletedListCommand = new listCommand( 'bulletedlist', 'ul' );
 651              editor.addCommand( 'numberedlist', numberedListCommand );
 652              editor.addCommand( 'bulletedlist', bulletedListCommand );
 653  
 654              // Register the toolbar button.

 655              editor.ui.addButton( 'NumberedList',
 656                  {
 657                      label : editor.lang.numberedlist,
 658                      command : 'numberedlist'
 659                  } );
 660              editor.ui.addButton( 'BulletedList',
 661                  {
 662                      label : editor.lang.bulletedlist,
 663                      command : 'bulletedlist'
 664                  } );
 665  
 666              // Register the state changing handlers.

 667              editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, numberedListCommand ) );
 668              editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, bulletedListCommand ) );
 669          },
 670  
 671          afterInit : function ( editor )
 672          {
 673              var dataProcessor = editor.dataProcessor;
 674              if ( dataProcessor )
 675              {
 676                  dataProcessor.dataFilter.addRules( defaultListDataFilterRules );
 677                  dataProcessor.htmlFilter.addRules( defaultListHtmlFilterRules );
 678              }
 679          },
 680  
 681          requires : [ 'domiterator' ]
 682      } );
 683  })();


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