2 Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved.
\r
3 For licensing, see LICENSE.html or http://ckeditor.com/license
\r
8 var guardElements = { table:1, ul:1, ol:1, blockquote:1, div:1 };
\r
9 var directSelectionGuardElements = {};
\r
10 CKEDITOR.tools.extend( directSelectionGuardElements, guardElements, { tr:1, p:1, div:1, li:1 } );
\r
12 function onSelectionChange( evt )
\r
14 var editor = evt.editor,
\r
15 path = evt.data.path;
\r
16 var useComputedState = editor.config.useComputedState,
\r
19 useComputedState = useComputedState === undefined || useComputedState;
\r
21 if ( useComputedState )
\r
23 var selection = editor.getSelection(),
\r
24 ranges = selection.getRanges();
\r
26 selectedElement = ranges && ranges[ 0 ].getEnclosedNode();
\r
28 // If this is not our element of interest, apply to fully selected elements from guardElements.
\r
29 if ( !selectedElement || selectedElement
\r
30 && !( selectedElement.type == CKEDITOR.NODE_ELEMENT && selectedElement.getName() in directSelectionGuardElements )
\r
32 selectedElement = getFullySelected( selection, guardElements );
\r
35 selectedElement = selectedElement || path.block || path.blockLimit;
\r
37 if ( !selectedElement || selectedElement.getName() == 'body' )
\r
40 var selectionDir = useComputedState ?
\r
41 selectedElement.getComputedStyle( 'direction' ) :
\r
42 selectedElement.getStyle( 'direction' ) || selectedElement.getAttribute( 'dir' );
\r
44 editor.getCommand( 'bidirtl' ).setState( selectionDir == 'rtl' ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );
\r
45 editor.getCommand( 'bidiltr' ).setState( selectionDir == 'ltr' ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF );
\r
47 var chromeRoot = editor.container.getChild( 1 );
\r
49 if ( selectionDir != editor.lang.dir )
\r
50 chromeRoot.addClass( 'cke_mixed_dir_content' );
\r
52 chromeRoot.removeClass( 'cke_mixed_dir_content' );
\r
55 function switchDir( element, dir, editor )
\r
57 var dirBefore = element.getComputedStyle( 'direction' ),
\r
58 currentDir = element.getStyle( 'direction' ) || element.getAttribute( 'dir' ) || '';
\r
60 element.removeStyle( 'direction' );
\r
62 if ( currentDir.toLowerCase() == dir )
\r
63 element.removeAttribute( 'dir' );
\r
65 element.setAttribute( 'dir', dir );
\r
67 // If the element direction changed, we need to switch the margins of
\r
68 // the element and all its children, so it will get really reflected
\r
69 // like a mirror. (#5910)
\r
70 var dirAfter = element.getComputedStyle( 'direction' );
\r
71 if ( dirAfter != dirBefore )
\r
73 var range = new CKEDITOR.dom.range( element.getDocument() );
\r
74 range.setStartBefore( element );
\r
75 range.setEndAfter( element );
\r
77 var walker = new CKEDITOR.dom.walker( range );
\r
80 while ( ( node = walker.next() ) )
\r
82 if ( node.type == CKEDITOR.NODE_ELEMENT )
\r
84 // A child with dir defined is to be ignored.
\r
85 if ( !node.equals( element ) && node.hasAttribute( 'dir' ) )
\r
87 range.setStartAfter( node );
\r
88 walker = new CKEDITOR.dom.walker( range );
\r
92 // Switch the margins.
\r
93 var marginLeft = node.getStyle( 'margin-right' ),
\r
94 marginRight = node.getStyle( 'margin-left' );
\r
96 marginLeft ? node.setStyle( 'margin-left', marginLeft ) : node.removeStyle( 'margin-left' );
\r
97 marginRight ? node.setStyle( 'margin-right', marginRight ) : node.removeStyle( 'margin-right' );
\r
102 editor.forceNextSelectionCheck();
\r
105 function getFullySelected( selection, elements )
\r
107 var selectedElement = selection.getCommonAncestor();
\r
108 while( selectedElement.type == CKEDITOR.NODE_ELEMENT
\r
109 && !( selectedElement.getName() in elements )
\r
110 && selectedElement.getParent().getChildCount() == 1
\r
112 selectedElement = selectedElement.getParent();
\r
114 return selectedElement.type == CKEDITOR.NODE_ELEMENT
\r
115 && ( selectedElement.getName() in elements )
\r
116 && selectedElement;
\r
119 function bidiCommand( dir )
\r
121 return function( editor )
\r
123 var selection = editor.getSelection(),
\r
124 enterMode = editor.config.enterMode,
\r
125 ranges = selection.getRanges();
\r
127 if ( ranges && ranges.length )
\r
129 // Apply do directly selected elements from guardElements.
\r
130 var selectedElement = ranges[ 0 ].getEnclosedNode();
\r
132 // If this is not our element of interest, apply to fully selected elements from guardElements.
\r
133 if ( !selectedElement || selectedElement
\r
134 && !( selectedElement.type == CKEDITOR.NODE_ELEMENT && selectedElement.getName() in directSelectionGuardElements )
\r
136 selectedElement = getFullySelected( selection, guardElements );
\r
138 if ( selectedElement )
\r
140 if ( !selectedElement.isReadOnly() )
\r
141 switchDir( selectedElement, dir, editor );
\r
145 // Creates bookmarks for selection, as we may split some blocks.
\r
146 var bookmarks = selection.createBookmarks();
\r
151 for ( var i = ranges.length - 1 ; i >= 0 ; i-- )
\r
153 // Array of elements processed as guardElements.
\r
154 var processedElements = [];
\r
155 // Walker searching for guardElements.
\r
156 var walker = new CKEDITOR.dom.walker( ranges[ i ] );
\r
157 walker.evaluator = function( node ){
\r
158 return node.type == CKEDITOR.NODE_ELEMENT
\r
159 && node.getName() in guardElements
\r
160 && !( node.getName() == ( enterMode == CKEDITOR.ENTER_P ) ? 'p' : 'div'
\r
161 && node.getParent().type == CKEDITOR.NODE_ELEMENT
\r
162 && node.getParent().getName() == 'blockquote'
\r
166 while ( ( block = walker.next() ) )
\r
168 switchDir( block, dir, editor );
\r
169 processedElements.push( block );
\r
172 iterator = ranges[ i ].createIterator();
\r
173 iterator.enlargeBr = enterMode != CKEDITOR.ENTER_BR;
\r
175 while ( ( block = iterator.getNextParagraph( enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ) ) )
\r
177 if ( block.isReadOnly() )
\r
182 // Check if block have been already processed by the walker above.
\r
183 for ( var ii = 0; ii < processedElements.length; ii++ )
\r
185 var parent = block.getParent();
\r
187 while( parent && parent.getName() != 'body' )
\r
189 if ( ( parent.$.isSameNode && parent.$.isSameNode( processedElements[ ii ].$ ) )
\r
190 || parent.$ == processedElements[ ii ].$ )
\r
195 parent = parent.getParent();
\r
204 switchDir( block, dir, editor );
\r
209 editor.forceNextSelectionCheck();
\r
210 // Restore selection position.
\r
211 selection.selectBookmarks( bookmarks );
\r
219 CKEDITOR.plugins.add( 'bidi',
\r
221 requires : [ 'styles', 'button' ],
\r
223 init : function( editor )
\r
225 // All buttons use the same code to register. So, to avoid
\r
226 // duplications, let's use this tool function.
\r
227 var addButtonCommand = function( buttonName, buttonLabel, commandName, commandExec )
\r
229 editor.addCommand( commandName, new CKEDITOR.command( editor, { exec : commandExec }) );
\r
231 editor.ui.addButton( buttonName,
\r
233 label : buttonLabel,
\r
234 command : commandName
\r
238 var lang = editor.lang.bidi;
\r
240 addButtonCommand( 'BidiLtr', lang.ltr, 'bidiltr', bidiCommand( 'ltr' ) );
\r
241 addButtonCommand( 'BidiRtl', lang.rtl, 'bidirtl', bidiCommand( 'rtl' ) );
\r
243 editor.on( 'selectionChange', onSelectionChange );
\r