JasonWoof Got questions, comments, patches, etc.? Contact Jason Woofenden
vanilla ckeditor-3.3
[ckeditor.git] / _source / plugins / indent / plugin.js
index e9992bd..80fc71d 100644 (file)
@@ -11,6 +11,9 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
 {\r
        var listNodeNames = { ol : 1, ul : 1 };\r
 \r
+       var isNotWhitespaces = CKEDITOR.dom.walker.whitespaces( true ),\r
+               isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true );\r
+\r
        function setState( editor, state )\r
        {\r
                editor.getCommand( this.name ).setState( state );\r
@@ -18,38 +21,13 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
 \r
        function onSelectionChange( evt )\r
        {\r
-               var elements = evt.data.path.elements,\r
-                       listNode, listItem,\r
-                       editor = evt.editor;\r
+               var editor = evt.editor;\r
 \r
-               for ( var i = 0 ; i < elements.length ; i++ )\r
-               {\r
-                       if ( elements[i].getName() == 'li' )\r
-                       {\r
-                               listItem = elements[i];\r
-                               continue;\r
-                       }\r
-                       if ( listNodeNames[ elements[i].getName() ] )\r
-                       {\r
-                               listNode = elements[i];\r
-                               break;\r
-                       }\r
-               }\r
+               var elementPath = evt.data.path,\r
+                               list = elementPath && elementPath.contains( listNodeNames );\r
 \r
-               if ( listNode )\r
-               {\r
-                       if ( this.name == 'outdent' )\r
+               if ( list )\r
                                return setState.call( this, editor, CKEDITOR.TRISTATE_OFF );\r
-                       else\r
-                       {\r
-                               while ( listItem && ( listItem = listItem.getPrevious( CKEDITOR.dom.walker.whitespaces( true ) ) ) )\r
-                               {\r
-                                       if ( listItem.getName && listItem.getName() == 'li' )\r
-                                               return setState.call( this, editor, CKEDITOR.TRISTATE_OFF );\r
-                               }\r
-                               return setState.call( this, editor, CKEDITOR.TRISTATE_DISABLED );\r
-                       }\r
-               }\r
 \r
                if ( !this.useIndentClasses && this.name == 'indent' )\r
                        return setState.call( this, editor, CKEDITOR.TRISTATE_OFF );\r
@@ -135,8 +113,14 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
 \r
                // Apply indenting or outdenting on the array.\r
                var baseIndent = listArray[ lastItem.getCustomData( 'listarray_index' ) ].indent;\r
-               for ( i = startItem.getCustomData( 'listarray_index' ) ; i <= lastItem.getCustomData( 'listarray_index' ) ; i++ )\r
-                       listArray[i].indent += indentOffset;\r
+               for ( i = startItem.getCustomData( 'listarray_index' ); i <= lastItem.getCustomData( 'listarray_index' ); i++ )\r
+               {\r
+                       listArray[ i ].indent += indentOffset;\r
+                       // Make sure the newly created sublist get a brand-new element of the same type. (#5372)\r
+                       var listRoot = listArray[ i ].parent;\r
+                       listArray[ i ].parent = new CKEDITOR.dom.element( listRoot.getName(), listRoot.getDocument() );\r
+               }\r
+\r
                for ( i = lastItem.getCustomData( 'listarray_index' ) + 1 ;\r
                                i < listArray.length && listArray[i].indent > baseIndent ; i++ )\r
                        listArray[i].indent += indentOffset;\r
@@ -181,6 +165,11 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                           followingList.is &&\r
                                           followingList.getName() in listNodeNames )\r
                                {\r
+                                       // IE requires a filler NBSP for nested list inside empty list item,\r
+                                       // otherwise the list item will be inaccessiable. (#4476)\r
+                                       if ( CKEDITOR.env.ie && !li.getFirst( function( node ){ return isNotWhitespaces( node ) && isNotBookmark( node ); } ) )\r
+                                               li.append( range.document.createText( '\u00a0' ) );\r
+\r
                                        li.append( followingList );\r
                                }\r
 \r
@@ -200,12 +189,15 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                iterator.enlargeBr = enterMode != CKEDITOR.ENTER_BR;\r
                var block;\r
                while ( ( block = iterator.getNextParagraph() ) )\r
-               {\r
+                       indentElement.call( this, editor, block );\r
+       }\r
 \r
+       function indentElement( editor, element )\r
+               {\r
                        if ( this.useIndentClasses )\r
                        {\r
                                // Transform current class name to indent step index.\r
-                               var indentClass = block.$.className.match( this.classNameRegex ),\r
+                       var indentClass = element.$.className.match( this.classNameRegex ),\r
                                        indentStep = 0;\r
                                if ( indentClass )\r
                                {\r
@@ -219,28 +211,37 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                                        indentStep--;\r
                                else\r
                                        indentStep++;\r
+\r
+                       if ( indentStep < 0 )\r
+                               return false;\r
+\r
                                indentStep = Math.min( indentStep, editor.config.indentClasses.length );\r
                                indentStep = Math.max( indentStep, 0 );\r
-                               var className = CKEDITOR.tools.ltrim( block.$.className.replace( this.classNameRegex, '' ) );\r
+                       var className = CKEDITOR.tools.ltrim( element.$.className.replace( this.classNameRegex, '' ) );\r
                                if ( indentStep < 1 )\r
-                                       block.$.className = className;\r
+                               element.$.className = className;\r
                                else\r
-                                       block.addClass( editor.config.indentClasses[ indentStep - 1 ] );\r
+                               element.addClass( editor.config.indentClasses[ indentStep - 1 ] );\r
                        }\r
                        else\r
                        {\r
-                               var currentOffset = parseInt( block.getStyle( this.indentCssProperty ), 10 );\r
+                       var currentOffset = parseInt( element.getStyle( this.indentCssProperty ), 10 );\r
                                if ( isNaN( currentOffset ) )\r
                                        currentOffset = 0;\r
                                currentOffset += ( this.name == 'indent' ? 1 : -1 ) * editor.config.indentOffset;\r
+\r
+                       if ( currentOffset < 0 )\r
+                               return false;\r
+\r
                                currentOffset = Math.max( currentOffset, 0 );\r
                                currentOffset = Math.ceil( currentOffset / editor.config.indentOffset ) * editor.config.indentOffset;\r
-                               block.setStyle( this.indentCssProperty, currentOffset ? currentOffset + editor.config.indentUnit : '' );\r
-                               if ( block.getAttribute( 'style' ) === '' )\r
-                                       block.removeAttribute( 'style' );\r
+                       element.setStyle( this.indentCssProperty, currentOffset ? currentOffset + editor.config.indentUnit : '' );\r
+                       if ( element.getAttribute( 'style' ) === '' )\r
+                               element.removeAttribute( 'style' );\r
                        }\r
+\r
+               return true;\r
                }\r
-       }\r
 \r
        function indentCommand( editor, name )\r
        {\r
@@ -255,6 +256,12 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                }\r
                else\r
                        this.indentCssProperty = editor.config.contentsLangDirection == 'ltr' ? 'margin-left' : 'margin-right';\r
+               this.startDisabled = name == 'outdent';\r
+       }\r
+\r
+       function isListItem( node )\r
+       {\r
+               return node.type = CKEDITOR.NODE_ELEMENT && node.is( 'li' );\r
        }\r
 \r
        indentCommand.prototype = {\r
@@ -263,18 +270,48 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                        var selection = editor.getSelection(),\r
                                range = selection && selection.getRanges()[0];\r
 \r
-                       if ( !selection || !range )\r
-                               return;\r
-\r
-                       var bookmarks = selection.createBookmarks( true ),\r
-                               nearestListBlock = range.getCommonAncestor();\r
+                       var startContainer = range.startContainer,\r
+                               endContainer = range.endContainer,\r
+                               rangeRoot = range.getCommonAncestor(),\r
+                               nearestListBlock = rangeRoot;\r
 \r
                        while ( nearestListBlock && !( nearestListBlock.type == CKEDITOR.NODE_ELEMENT &&\r
                                listNodeNames[ nearestListBlock.getName() ] ) )\r
                                nearestListBlock = nearestListBlock.getParent();\r
 \r
-                       if ( nearestListBlock )\r
+                       // Avoid selection anchors under list root.\r
+                       // <ul>[<li>...</li>]</ul> =>   <ul><li>[...]</li></ul>\r
+                       if ( nearestListBlock && startContainer.type == CKEDITOR.NODE_ELEMENT\r
+                               && startContainer.getName() in listNodeNames )\r
+                       {\r
+                               var walker = new CKEDITOR.dom.walker( range );\r
+                               walker.evaluator = isListItem;\r
+                               range.startContainer = walker.next();\r
+                       }\r
+\r
+                       if ( nearestListBlock && endContainer.type == CKEDITOR.NODE_ELEMENT\r
+                               && endContainer.getName() in listNodeNames )\r
+                       {\r
+                               walker = new CKEDITOR.dom.walker( range );\r
+                               walker.evaluator = isListItem;\r
+                               range.endContainer = walker.previous();\r
+                       }\r
+\r
+                       var bookmarks = selection.createBookmarks( true );\r
+\r
+                       if ( nearestListBlock  )\r
+                       {\r
+                               var firstListItem = nearestListBlock.getFirst( function( node )\r
+                                       {\r
+                                               return node.type == CKEDITOR.NODE_ELEMENT && node.is( 'li' );\r
+                                       }),\r
+                                       rangeStart = range.startContainer,\r
+                                       indentWholeList = firstListItem.equals( rangeStart ) || firstListItem.contains( rangeStart );\r
+\r
+                               // Indent the entire list if  cursor is inside the first list item. (#3893)\r
+                               if ( !( indentWholeList && indentElement.call( this, editor, nearestListBlock ) ) )\r
                                indentList.call( this, editor, range, nearestListBlock );\r
+                       }\r
                        else\r
                                indentBlock.call( this, editor, range );\r
 \r
@@ -309,6 +346,17 @@ For licensing, see LICENSE.html or http://ckeditor.com/license
                        // Register the state changing handlers.\r
                        editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, indent ) );\r
                        editor.on( 'selectionChange', CKEDITOR.tools.bind( onSelectionChange, outdent ) );\r
+\r
+                       // [IE6/7] Raw lists are using margin instead of padding for visual indentation in wysiwyg mode. (#3893)\r
+                       if ( CKEDITOR.env.ie6Compat || CKEDITOR.env.ie7Compat )\r
+                       {\r
+                               editor.addCss(\r
+                                       "ul,ol" +\r
+                                       "{" +\r
+                                       "       margin-left: 0px;" +\r
+                                       "       padding-left: 40px;" +\r
+                                       "}" );\r
+                       }\r
                },\r
 \r
                requires : [ 'domiterator', 'list' ]\r
@@ -325,6 +373,31 @@ CKEDITOR.tools.extend( CKEDITOR.config,
 /**\r
  * Size of each indentation step\r
  * @type Number\r
+ * @example\r
+ * config.indentOffset = 40;\r
+ */\r
+\r
+ /**\r
+ * Unit for the indentation style\r
+ * @type String\r
+ * @example\r
+ * config.indentUnit = 'px';\r
+ */\r
+\r
+ /**\r
+ * List of classes to use for indenting the contents.\r
+ * @type Array\r
+ * @example\r
+ * // Don't use classes for indenting. (this is the default value)\r
+ * config.indentClasses = null;\r
+ * @example\r
+ * // Use the classes 'Indent1', 'Indent2', 'Indent3'\r
+ * config.indentClasses = ['Indent1', 'Indent2', 'Indent3'];\r
+ */\r
+\r
+/**\r
+ * Size of each indentation step\r
+ * @type Number\r
  * @default 40\r
  * @example\r
  * config.indentOffset = 4;\r