| [ Index ] |
PHP Cross Reference of Drupal 6 (gatewave) |
[Summary view] [Print] [Text view]
1 /* 2 Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. 3 For licensing, see LICENSE.html or http://ckeditor.com/license 4 */ 5 6 /** 7 * @file Increse and decrease indent commands. 8 */ 9 10 (function() 11 { 12 var listNodeNames = { ol : 1, ul : 1 }; 13 14 var isNotWhitespaces = CKEDITOR.dom.walker.whitespaces( true ), 15 isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true ); 16 17 function setState( editor, state ) 18 { 19 editor.getCommand( this.name ).setState( state ); 20 } 21 22 function onSelectionChange( evt ) 23 { 24 var editor = evt.editor; 25 26 var elementPath = evt.data.path, 27 list = elementPath && elementPath.contains( listNodeNames ); 28 29 if ( list ) 30 return setState.call( this, editor, CKEDITOR.TRISTATE_OFF ); 31 32 if ( !this.useIndentClasses && this.name == 'indent' ) 33 return setState.call( this, editor, CKEDITOR.TRISTATE_OFF ); 34 35 var path = evt.data.path, 36 firstBlock = path.block || path.blockLimit; 37 if ( !firstBlock ) 38 return setState.call( this, editor, CKEDITOR.TRISTATE_DISABLED ); 39 40 if ( this.useIndentClasses ) 41 { 42 var indentClass = firstBlock.$.className.match( this.classNameRegex ), 43 indentStep = 0; 44 if ( indentClass ) 45 { 46 indentClass = indentClass[1]; 47 indentStep = this.indentClassMap[ indentClass ]; 48 } 49 if ( ( this.name == 'outdent' && !indentStep ) || 50 ( this.name == 'indent' && indentStep == editor.config.indentClasses.length ) ) 51 return setState.call( this, editor, CKEDITOR.TRISTATE_DISABLED ); 52 return setState.call( this, editor, CKEDITOR.TRISTATE_OFF ); 53 } 54 else 55 { 56 var indent = parseInt( firstBlock.getStyle( getIndentCssProperty( firstBlock ) ), 10 ); 57 if ( isNaN( indent ) ) 58 indent = 0; 59 if ( indent <= 0 ) 60 return setState.call( this, editor, CKEDITOR.TRISTATE_DISABLED ); 61 return setState.call( this, editor, CKEDITOR.TRISTATE_OFF ); 62 } 63 } 64 65 function indentCommand( editor, name ) 66 { 67 this.name = name; 68 this.useIndentClasses = editor.config.indentClasses && editor.config.indentClasses.length > 0; 69 if ( this.useIndentClasses ) 70 { 71 this.classNameRegex = new RegExp( '(?:^|\\s+)(' + editor.config.indentClasses.join( '|' ) + ')(?=$|\\s)' ); 72 this.indentClassMap = {}; 73 for ( var i = 0 ; i < editor.config.indentClasses.length ; i++ ) 74 this.indentClassMap[ editor.config.indentClasses[i] ] = i + 1; 75 } 76 77 this.startDisabled = name == 'outdent'; 78 } 79 80 // Returns the CSS property to be used for identing a given element. 81 function getIndentCssProperty( element ) 82 { 83 return element.getComputedStyle( 'direction' ) == 'ltr' ? 'margin-left' : 'margin-right'; 84 } 85 86 function isListItem( node ) 87 { 88 return node.type = CKEDITOR.NODE_ELEMENT && node.is( 'li' ); 89 } 90 91 indentCommand.prototype = { 92 exec : function( editor ) 93 { 94 var self = this, database = {}; 95 96 function indentList( listNode ) 97 { 98 // Our starting and ending points of the range might be inside some blocks under a list item... 99 // So before playing with the iterator, we need to expand the block to include the list items. 100 var startContainer = range.startContainer, 101 endContainer = range.endContainer; 102 while ( startContainer && !startContainer.getParent().equals( listNode ) ) 103 startContainer = startContainer.getParent(); 104 while ( endContainer && !endContainer.getParent().equals( listNode ) ) 105 endContainer = endContainer.getParent(); 106 107 if ( !startContainer || !endContainer ) 108 return; 109 110 // Now we can iterate over the individual items on the same tree depth. 111 var block = startContainer, 112 itemsToMove = [], 113 stopFlag = false; 114 while ( !stopFlag ) 115 { 116 if ( block.equals( endContainer ) ) 117 stopFlag = true; 118 itemsToMove.push( block ); 119 block = block.getNext(); 120 } 121 if ( itemsToMove.length < 1 ) 122 return; 123 124 // Do indent or outdent operations on the array model of the list, not the 125 // list's DOM tree itself. The array model demands that it knows as much as 126 // possible about the surrounding lists, we need to feed it the further 127 // ancestor node that is still a list. 128 var listParents = listNode.getParents( true ); 129 for ( var i = 0 ; i < listParents.length ; i++ ) 130 { 131 if ( listParents[i].getName && listNodeNames[ listParents[i].getName() ] ) 132 { 133 listNode = listParents[i]; 134 break; 135 } 136 } 137 var indentOffset = self.name == 'indent' ? 1 : -1, 138 startItem = itemsToMove[0], 139 lastItem = itemsToMove[ itemsToMove.length - 1 ]; 140 141 // Convert the list DOM tree into a one dimensional array. 142 var listArray = CKEDITOR.plugins.list.listToArray( listNode, database ); 143 144 // Apply indenting or outdenting on the array. 145 var baseIndent = listArray[ lastItem.getCustomData( 'listarray_index' ) ].indent; 146 for ( i = startItem.getCustomData( 'listarray_index' ); i <= lastItem.getCustomData( 'listarray_index' ); i++ ) 147 { 148 listArray[ i ].indent += indentOffset; 149 // Make sure the newly created sublist get a brand-new element of the same type. (#5372) 150 var listRoot = listArray[ i ].parent; 151 listArray[ i ].parent = new CKEDITOR.dom.element( listRoot.getName(), listRoot.getDocument() ); 152 } 153 154 for ( i = lastItem.getCustomData( 'listarray_index' ) + 1 ; 155 i < listArray.length && listArray[i].indent > baseIndent ; i++ ) 156 listArray[i].indent += indentOffset; 157 158 // Convert the array back to a DOM forest (yes we might have a few subtrees now). 159 // And replace the old list with the new forest. 160 var newList = CKEDITOR.plugins.list.arrayToList( listArray, database, null, editor.config.enterMode, 0 ); 161 162 // Avoid nested <li> after outdent even they're visually same, 163 // recording them for later refactoring.(#3982) 164 if ( self.name == 'outdent' ) 165 { 166 var parentLiElement; 167 if ( ( parentLiElement = listNode.getParent() ) && parentLiElement.is( 'li' ) ) 168 { 169 var children = newList.listNode.getChildren(), 170 pendingLis = [], 171 count = children.count(), 172 child; 173 174 for ( i = count - 1 ; i >= 0 ; i-- ) 175 { 176 if ( ( child = children.getItem( i ) ) && child.is && child.is( 'li' ) ) 177 pendingLis.push( child ); 178 } 179 } 180 } 181 182 if ( newList ) 183 newList.listNode.replace( listNode ); 184 185 // Move the nested <li> to be appeared after the parent. 186 if ( pendingLis && pendingLis.length ) 187 { 188 for ( i = 0; i < pendingLis.length ; i++ ) 189 { 190 var li = pendingLis[ i ], 191 followingList = li; 192 193 // Nest preceding <ul>/<ol> inside current <li> if any. 194 while ( ( followingList = followingList.getNext() ) && 195 followingList.is && 196 followingList.getName() in listNodeNames ) 197 { 198 // IE requires a filler NBSP for nested list inside empty list item, 199 // otherwise the list item will be inaccessiable. (#4476) 200 if ( CKEDITOR.env.ie && !li.getFirst( function( node ){ return isNotWhitespaces( node ) && isNotBookmark( node ); } ) ) 201 li.append( range.document.createText( '\u00a0' ) ); 202 203 li.append( followingList ); 204 } 205 206 li.insertAfter( parentLiElement ); 207 } 208 } 209 } 210 211 function indentBlock() 212 { 213 var iterator = range.createIterator(), 214 enterMode = editor.config.enterMode; 215 iterator.enforceRealBlocks = true; 216 iterator.enlargeBr = enterMode != CKEDITOR.ENTER_BR; 217 var block; 218 while ( ( block = iterator.getNextParagraph() ) ) 219 indentElement( block ); 220 } 221 222 function indentElement( element ) 223 { 224 if ( element.getCustomData( 'indent_processed' ) ) 225 return false; 226 227 if ( self.useIndentClasses ) 228 { 229 // Transform current class name to indent step index. 230 var indentClass = element.$.className.match( self.classNameRegex ), 231 indentStep = 0; 232 if ( indentClass ) 233 { 234 indentClass = indentClass[1]; 235 indentStep = self.indentClassMap[ indentClass ]; 236 } 237 238 // Operate on indent step index, transform indent step index back to class 239 // name. 240 if ( self.name == 'outdent' ) 241 indentStep--; 242 else 243 indentStep++; 244 245 if ( indentStep < 0 ) 246 return false; 247 248 indentStep = Math.min( indentStep, editor.config.indentClasses.length ); 249 indentStep = Math.max( indentStep, 0 ); 250 var className = CKEDITOR.tools.ltrim( element.$.className.replace( self.classNameRegex, '' ) ); 251 if ( indentStep < 1 ) 252 element.$.className = className; 253 else 254 element.addClass( editor.config.indentClasses[ indentStep - 1 ] ); 255 } 256 else 257 { 258 var indentCssProperty = getIndentCssProperty( element ); 259 var currentOffset = parseInt( element.getStyle( indentCssProperty ), 10 ); 260 if ( isNaN( currentOffset ) ) 261 currentOffset = 0; 262 currentOffset += ( self.name == 'indent' ? 1 : -1 ) * editor.config.indentOffset; 263 264 if ( currentOffset < 0 ) 265 return false; 266 267 currentOffset = Math.max( currentOffset, 0 ); 268 currentOffset = Math.ceil( currentOffset / editor.config.indentOffset ) * editor.config.indentOffset; 269 element.setStyle( indentCssProperty, currentOffset ? currentOffset + editor.config.indentUnit : '' ); 270 if ( element.getAttribute( 'style' ) === '' ) 271 element.removeAttribute( 'style' ); 272 } 273 274 CKEDITOR.dom.element.setMarker( database, element, 'indent_processed', true ); 275 return true; 276 } 277 278 var selection = editor.getSelection(), 279 bookmarks = selection.createBookmarks( true ), 280 ranges = selection && selection.getRanges( true ), 281 range; 282 283 var iterator = ranges.createIterator(); 284 while ( ( range = iterator.getNextRange() ) ) 285 { 286 var startContainer = range.startContainer, 287 endContainer = range.endContainer, 288 rangeRoot = range.getCommonAncestor(), 289 nearestListBlock = rangeRoot; 290 291 while ( nearestListBlock && !( nearestListBlock.type == CKEDITOR.NODE_ELEMENT && 292 listNodeNames[ nearestListBlock.getName() ] ) ) 293 nearestListBlock = nearestListBlock.getParent(); 294 295 // Avoid selection anchors under list root. 296 // <ul>[<li>...</li>]</ul> => <ul><li>[...]</li></ul> 297 if ( nearestListBlock && startContainer.type == CKEDITOR.NODE_ELEMENT 298 && startContainer.getName() in listNodeNames ) 299 { 300 var walker = new CKEDITOR.dom.walker( range ); 301 walker.evaluator = isListItem; 302 range.startContainer = walker.next(); 303 } 304 305 if ( nearestListBlock && endContainer.type == CKEDITOR.NODE_ELEMENT 306 && endContainer.getName() in listNodeNames ) 307 { 308 walker = new CKEDITOR.dom.walker( range ); 309 walker.evaluator = isListItem; 310 range.endContainer = walker.previous(); 311 } 312 313 if ( nearestListBlock ) 314 { 315 var firstListItem = nearestListBlock.getFirst( function( node ) 316 { 317 return node.type == CKEDITOR.NODE_ELEMENT && node.is( 'li' ); 318 }), 319 rangeStart = range.startContainer, 320 indentWholeList = firstListItem.equals( rangeStart ) || firstListItem.contains( rangeStart ); 321 322 // Indent the entire list if cursor is inside the first list item. (#3893) 323 if ( !( indentWholeList && indentElement( nearestListBlock ) ) ) 324 indentList( nearestListBlock ); 325 } 326 else 327 indentBlock(); 328 } 329 330 // Clean up the markers. 331 CKEDITOR.dom.element.clearAllMarkers( database ); 332 333 editor.forceNextSelectionCheck(); 334 selection.selectBookmarks( bookmarks ); 335 } 336 }; 337 338 CKEDITOR.plugins.add( 'indent', 339 { 340 init : function( editor ) 341 { 342 // Register commands. 343 var indent = new indentCommand( editor, 'indent' ), 344 outdent = new indentCommand( editor, 'outdent' ); 345 editor.addCommand( 'indent', indent ); 346 editor.addCommand( 'outdent', outdent ); 347 348 // Register the toolbar buttons. 349 editor.ui.addButton( 'Indent', 350 { 351 label : editor.lang.indent, 352 command : 'indent' 353 }); 354 editor.ui.addButton( 'Outdent', 355 { 356 label : editor.lang.outdent, 357 command : 'outdent' 358 }); 359 360 // Register the state changing handlers. 361 editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, indent ) ); 362 editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, outdent ) ); 363 364 // [IE6/7] Raw lists are using margin instead of padding for visual indentation in wysiwyg mode. (#3893) 365 if ( CKEDITOR.env.ie6Compat || CKEDITOR.env.ie7Compat ) 366 { 367 editor.addCss( 368 "ul,ol" + 369 "{" + 370 " margin-left: 0px;" + 371 " padding-left: 40px;" + 372 "}" ); 373 } 374 }, 375 376 requires : [ 'domiterator', 'list' ] 377 } ); 378 })(); 379 380 CKEDITOR.tools.extend( CKEDITOR.config, 381 { 382 indentOffset : 40, 383 indentUnit : 'px', 384 indentClasses : null 385 }); 386 387 /** 388 * Size of each indentation step 389 * @type Number 390 * @example 391 * config.indentOffset = 40; 392 */ 393 394 /** 395 * Unit for the indentation style 396 * @type String 397 * @example 398 * config.indentUnit = 'px'; 399 */ 400 401 /** 402 * List of classes to use for indenting the contents. 403 * @type Array 404 * @example 405 * // Don't use classes for indenting. (this is the default value) 406 * config.indentClasses = null; 407 * @example 408 * // Use the classes 'Indent1', 'Indent2', 'Indent3' 409 * config.indentClasses = ['Indent1', 'Indent2', 'Indent3']; 410 */ 411 412 /** 413 * Size of each indentation step 414 * @type Number 415 * @default 40 416 * @example 417 * config.indentOffset = 4; 418 */ 419 420 /** 421 * Unit for the indentation style 422 * @type String 423 * @default 'px' 424 * @example 425 * config.indentUnit = 'em'; 426 */ 427 428 /** 429 * List of classes to use for indenting the contents. If it's null, no classes will be used 430 * and instead the {@link #indentUnit} and {@link #indentOffset} properties will be used. 431 * @type Array 432 * default null 433 * @example 434 * // Use the classes 'Indent1', 'Indent2', 'Indent3' 435 * config.indentClasses = ['Indent1', 'Indent2', 'Indent3']; 436 */
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Thu Mar 24 11:18:33 2011 | Cross-referenced by PHPXref 0.7 |