/* Copyright (c) 2003-2010, CKSource - Frederico Knabben. All rights reserved. For licensing, see LICENSE.html or http://ckeditor.com/license */ (function() { var guardElements = { table:1, ul:1, ol:1, blockquote:1, div:1 }; var directSelectionGuardElements = {}; CKEDITOR.tools.extend( directSelectionGuardElements, guardElements, { tr:1, p:1, div:1, li:1 } ); function onSelectionChange( evt ) { evt.editor.getCommand( 'bidirtl' ).setState( getState( evt.editor, evt.data.path, 'rtl' ) ); evt.editor.getCommand( 'bidiltr' ).setState( getState( evt.editor, evt.data.path, 'ltr' ) ); } function getState( editor, path, dir ) { var useComputedState = editor.config.useComputedState, selectedElement; useComputedState = useComputedState === undefined || useComputedState; if ( useComputedState ) { var selection = editor.getSelection(), ranges = selection.getRanges(); selectedElement = ranges && ranges[ 0 ].getEnclosedNode(); // If this is not our element of interest, apply to fully selected elements from guardElements. if ( !selectedElement || selectedElement && !( selectedElement.type == CKEDITOR.NODE_ELEMENT && selectedElement.getName() in directSelectionGuardElements ) ) selectedElement = getFullySelected( selection, guardElements ); } selectedElement = selectedElement || path.block || path.blockLimit; if ( !selectedElement || selectedElement.getName() == 'body' ) return CKEDITOR.TRISTATE_OFF; selectedElement = useComputedState ? selectedElement.getComputedStyle( 'direction' ) : selectedElement.getStyle( 'direction' ) || selectedElement.getAttribute( 'dir' ); return ( selectedElement == dir ) ? CKEDITOR.TRISTATE_ON : CKEDITOR.TRISTATE_OFF; } function switchDir( element, dir, editor ) { var dirBefore = element.getComputedStyle( 'direction' ), currentDir = element.getStyle( 'direction' ) || element.getAttribute( 'dir' ) || ''; element.removeStyle( 'direction' ); if ( currentDir.toLowerCase() == dir ) element.removeAttribute( 'dir' ); else element.setAttribute( 'dir', dir ); // If the element direction changed, we need to switch the margins of // the element and all its children, so it will get really reflected // like a mirror. (#5910) var dirAfter = element.getComputedStyle( 'direction' ); if ( dirAfter != dirBefore ) { var range = new CKEDITOR.dom.range( element.getDocument() ); range.setStartBefore( element ); range.setEndAfter( element ); var walker = new CKEDITOR.dom.walker( range ); var node; while ( ( node = walker.next() ) ) { if ( node.type == CKEDITOR.NODE_ELEMENT ) { // A child with dir defined is to be ignored. if ( !node.equals( element ) && node.hasAttribute( 'dir' ) ) { range.setStartAfter( node ); walker = new CKEDITOR.dom.walker( range ); continue; } // Switch the margins. var marginLeft = node.getStyle( 'margin-right' ), marginRight = node.getStyle( 'margin-left' ); marginLeft ? node.setStyle( 'margin-left', marginLeft ) : node.removeStyle( 'margin-left' ); marginRight ? node.setStyle( 'margin-right', marginRight ) : node.removeStyle( 'margin-right' ); } } } editor.forceNextSelectionCheck(); } function getFullySelected( selection, elements ) { var selectedElement = selection.getCommonAncestor(); while( selectedElement.type == CKEDITOR.NODE_ELEMENT && !( selectedElement.getName() in elements ) && selectedElement.getParent().getChildCount() == 1 ) selectedElement = selectedElement.getParent(); return selectedElement.type == CKEDITOR.NODE_ELEMENT && ( selectedElement.getName() in elements ) && selectedElement; } function bidiCommand( dir ) { return function( editor ) { var selection = editor.getSelection(), enterMode = editor.config.enterMode, ranges = selection.getRanges(); if ( ranges && ranges.length ) { // Apply do directly selected elements from guardElements. var selectedElement = ranges[ 0 ].getEnclosedNode(); // If this is not our element of interest, apply to fully selected elements from guardElements. if ( !selectedElement || selectedElement && !( selectedElement.type == CKEDITOR.NODE_ELEMENT && selectedElement.getName() in directSelectionGuardElements ) ) selectedElement = getFullySelected( selection, guardElements ); if ( selectedElement ) { if ( !selectedElement.isReadOnly() ) switchDir( selectedElement, dir, editor ); } else { // Creates bookmarks for selection, as we may split some blocks. var bookmarks = selection.createBookmarks(); var iterator, block; for ( var i = ranges.length - 1 ; i >= 0 ; i-- ) { // Array of elements processed as guardElements. var processedElements = []; // Walker searching for guardElements. var walker = new CKEDITOR.dom.walker( ranges[ i ] ); walker.evaluator = function( node ){ return node.type == CKEDITOR.NODE_ELEMENT && node.getName() in guardElements && !( node.getName() == ( enterMode == CKEDITOR.ENTER_P ) ? 'p' : 'div' && node.getParent().type == CKEDITOR.NODE_ELEMENT && node.getParent().getName() == 'blockquote' ); }; while ( ( block = walker.next() ) ) { switchDir( block, dir, editor ); processedElements.push( block ); } iterator = ranges[ i ].createIterator(); iterator.enlargeBr = enterMode != CKEDITOR.ENTER_BR; while ( ( block = iterator.getNextParagraph( enterMode == CKEDITOR.ENTER_P ? 'p' : 'div' ) ) ) { if ( block.isReadOnly() ) continue; var _break = 0; // Check if block have been already processed by the walker above. for ( var ii = 0; ii < processedElements.length; ii++ ) { var parent = block.getParent(); while( parent && parent.getName() != 'body' ) { if ( ( parent.$.isSameNode && parent.$.isSameNode( processedElements[ ii ].$ ) ) || parent.$ == processedElements[ ii ].$ ) { _break = 1; break; } parent = parent.getParent(); } if ( _break ) break; } if ( !_break ) { switchDir( block, dir, editor ); } } } editor.forceNextSelectionCheck(); // Restore selection position. selection.selectBookmarks( bookmarks ); } editor.focus(); } }; } CKEDITOR.plugins.add( 'bidi', { requires : [ 'styles', 'button' ], init : function( editor ) { // All buttons use the same code to register. So, to avoid // duplications, let's use this tool function. var addButtonCommand = function( buttonName, buttonLabel, commandName, commandExec ) { editor.addCommand( commandName, new CKEDITOR.command( editor, { exec : commandExec }) ); editor.ui.addButton( buttonName, { label : buttonLabel, command : commandName }); }; var lang = editor.lang.bidi; addButtonCommand( 'BidiLtr', lang.ltr, 'bidiltr', bidiCommand( 'ltr' ) ); addButtonCommand( 'BidiRtl', lang.rtl, 'bidirtl', bidiCommand( 'rtl' ) ); editor.on( 'selectionChange', onSelectionChange ); } }); })();