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 evt.editor.getCommand( 'bidirtl' ).setState( getState( evt.editor, evt.data.path, 'rtl' ) );
\r
15 evt.editor.getCommand( 'bidiltr' ).setState( getState( evt.editor, evt.data.path, 'ltr' ) );
\r
18 function getState( editor, path, dir )
\r
20 var useComputedState = editor.config.useComputedState,
\r
23 useComputedState = useComputedState === undefined || useComputedState;
\r
25 if ( useComputedState )
\r
27 var selection = editor.getSelection(),
\r
28 ranges = selection.getRanges();
\r
30 selectedElement = ranges && ranges[ 0 ].getEnclosedNode();
\r
32 // If this is not our element of interest, apply to fully selected elements from guardElements.
\r
33 if ( !selectedElement || selectedElement
\r
34 && !( selectedElement.type == CKEDITOR.NODE_ELEMENT && selectedElement.getName() in directSelectionGuardElements )
\r
36 selectedElement = getFullySelected( selection, guardElements );
\r
39 selectedElement = selectedElement || path.block || path.blockLimit;
\r
41 if ( !selectedElement || selectedElement.getName() == 'body' )
\r
42 return CKEDITOR.TRISTATE_OFF;
\r
44 selectedElement = useComputedState ?
\r
45 selectedElement.getComputedStyle( 'direction' ) :
\r
46 selectedElement.getStyle( 'direction' ) || selectedElement.getAttribute( 'dir' );
\r
48 return ( selectedElement == dir ) ?
\r
49 CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF;
\r
52 function switchDir( element, dir, editor )
\r
54 var dirBefore = element.getComputedStyle( 'direction' ),
\r
55 currentDir = element.getStyle( 'direction' ) || element.getAttribute( 'dir' ) || '';
\r
57 element.removeStyle( 'direction' );
\r
59 if ( currentDir.toLowerCase() == dir )
\r
60 element.removeAttribute( 'dir' );
\r
62 element.setAttribute( 'dir', dir );
\r
64 // If the element direction changed, we need to switch the margins of
\r
65 // the element and all its children, so it will get really reflected
\r
66 // like a mirror. (#5910)
\r
67 var dirAfter = element.getComputedStyle( 'direction' );
\r
68 if ( dirAfter != dirBefore )
\r
70 var range = new CKEDITOR.dom.range( element.getDocument() );
\r
71 range.setStartBefore( element );
\r
72 range.setEndAfter( element );
\r
74 var walker = new CKEDITOR.dom.walker( range );
\r
77 while ( ( node = walker.next() ) )
\r
79 if ( node.type == CKEDITOR.NODE_ELEMENT )
\r
81 // A child with dir defined is to be ignored.
\r
82 if ( !node.equals( element ) && node.hasAttribute( 'dir' ) )
\r
84 range.setStartAfter( node );
\r
85 walker = new CKEDITOR.dom.walker( range );
\r
89 // Switch the margins.
\r
90 var marginLeft = node.getStyle( 'margin-right' ),
\r
91 marginRight = node.getStyle( 'margin-left' );
\r
93 marginLeft ? node.setStyle( 'margin-left', marginLeft ) : node.removeStyle( 'margin-left' );
\r
94 marginRight ? node.setStyle( 'margin-right', marginRight ) : node.removeStyle( 'margin-right' );
\r
99 editor.forceNextSelectionCheck();
\r
102 function getFullySelected( selection, elements )
\r
104 var selectedElement = selection.getCommonAncestor();
\r
105 while( selectedElement.type == CKEDITOR.NODE_ELEMENT
\r
106 && !( selectedElement.getName() in elements )
\r
107 && selectedElement.getParent().getChildCount() == 1
\r
109 selectedElement = selectedElement.getParent();
\r
111 return selectedElement.type == CKEDITOR.NODE_ELEMENT
\r
112 && ( selectedElement.getName() in elements )
\r
113 && selectedElement;
\r
116 function bidiCommand( dir )
\r
118 return function( editor )
\r
120 var selection = editor.getSelection(),
\r
121 enterMode = editor.config.enterMode,
\r
122 ranges = selection.getRanges();
\r
124 if ( ranges && ranges.length )
\r
126 // Apply do directly selected elements from guardElements.
\r
127 var selectedElement = ranges[ 0 ].getEnclosedNode();
\r
129 // If this is not our element of interest, apply to fully selected elements from guardElements.
\r
130 if ( !selectedElement || selectedElement
\r
131 && !( selectedElement.type == CKEDITOR.NODE_ELEMENT && selectedElement.getName() in directSelectionGuardElements )
\r
133 selectedElement = getFullySelected( selection, guardElements );
\r
135 if ( selectedElement )
\r
137 if ( !selectedElement.isReadOnly() )
\r
138 switchDir( selectedElement, dir, editor );
\r
142 // Creates bookmarks for selection, as we may split some blocks.
\r
143 var bookmarks = selection.createBookmarks();
\r
148 for ( var i = ranges.length - 1 ; i >= 0 ; i-- )
\r
150 // Array of elements processed as guardElements.
\r
151 var processedElements = [];
\r
152 // Walker searching for guardElements.
\r
153 var walker = new CKEDITOR.dom.walker( ranges[ i ] );
\r
154 walker.evaluator = function( node ){
\r
155 return node.type == CKEDITOR.NODE_ELEMENT
\r
156 && node.getName() in guardElements
\r
157 && !( node.getName() == ( enterMode == CKEDITOR.ENTER_P ) ? 'p' : 'div'
\r
158 && node.getParent().type == CKEDITOR.NODE_ELEMENT
\r
159 && node.getParent().getName() == 'blockquote'
\r
163 while ( ( block = walker.next() ) )
\r
165 switchDir( block, dir, editor );
\r
166 processedElements.push( block );
\r
169 iterator = ranges[ i ].createIterator();
\r
170 iterator.enlargeBr = enterMode != CKEDITOR.ENTER_BR;
\r
172 while ( ( block = iterator.getNextParagraph( enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ) ) )
\r
174 if ( block.isReadOnly() )
\r
179 // Check if block have been already processed by the walker above.
\r
180 for ( var ii = 0; ii < processedElements.length; ii++ )
\r
182 var parent = block.getParent();
\r
184 while( parent && parent.getName() != 'body' )
\r
186 if ( ( parent.$.isSameNode && parent.$.isSameNode( processedElements[ ii ].$ ) )
\r
187 || parent.$ == processedElements[ ii ].$ )
\r
192 parent = parent.getParent();
\r
201 switchDir( block, dir, editor );
\r
206 editor.forceNextSelectionCheck();
\r
207 // Restore selection position.
\r
208 selection.selectBookmarks( bookmarks );
\r
216 CKEDITOR.plugins.add( 'bidi',
\r
218 requires : [ 'styles', 'button' ],
\r
220 init : function( editor )
\r
222 // All buttons use the same code to register. So, to avoid
\r
223 // duplications, let's use this tool function.
\r
224 var addButtonCommand = function( buttonName, buttonLabel, commandName, commandExec )
\r
226 editor.addCommand( commandName, new CKEDITOR.command( editor, { exec : commandExec }) );
\r
228 editor.ui.addButton( buttonName,
\r
230 label : buttonLabel,
\r
231 command : commandName
\r
235 var lang = editor.lang.bidi;
\r
237 addButtonCommand( 'BidiLtr', lang.ltr, 'bidiltr', bidiCommand( 'ltr' ) );
\r
238 addButtonCommand( 'BidiRtl', lang.rtl, 'bidirtl', bidiCommand( 'rtl' ) );
\r
240 editor.on( 'selectionChange', onSelectionChange );
\r