+\r
+ // Handled backspace/del key to join list items. (#8248,#9080)\r
+ editor.on( 'key', function( evt )\r
+ {\r
+ var key = evt.data.keyCode;\r
+\r
+ // DEl/BACKSPACE\r
+ if ( editor.mode == 'wysiwyg' && key in { 8 : 1, 46 : 1 } )\r
+ {\r
+ var sel = editor.getSelection(),\r
+ range = sel.getRanges()[ 0 ];\r
+\r
+ if ( !range.collapsed )\r
+ return;\r
+\r
+ var path = new CKEDITOR.dom.elementPath( range.startContainer );\r
+ var isBackspace = key == 8;\r
+ var body = editor.document.getBody();\r
+ var walker = new CKEDITOR.dom.walker( range.clone() );\r
+ walker.evaluator = function( node ) { return nonEmpty( node ) && !blockBogus( node ); };\r
+\r
+ // Backspace/Del behavior at the start/end of table is handled in core.\r
+ walker.guard = function( node, isOut ) { return !( isOut && node.type == CKEDITOR.NODE_ELEMENT && node.is( 'table' ) ); };\r
+\r
+ var cursor = range.clone();\r
+\r
+ if ( isBackspace )\r
+ {\r
+ var previous, joinWith;\r
+\r
+ // Join a sub list's first line, with the previous visual line in parent.\r
+ if ( ( previous = path.contains( listNodeNames ) ) &&\r
+ range.checkBoundaryOfElement( previous, CKEDITOR.START ) &&\r
+ ( previous = previous.getParent() ) && previous.is( 'li' ) &&\r
+ ( previous = getSubList( previous ) ) )\r
+ {\r
+ joinWith = previous;\r
+ previous = previous.getPrevious( nonEmpty );\r
+ // Place cursor before the nested list.\r
+ cursor.moveToPosition(\r
+ previous && blockBogus( previous ) ? previous : joinWith,\r
+ CKEDITOR.POSITION_BEFORE_START );\r
+ }\r
+ // Join any line following a list, with the last visual line of the list.\r
+ else\r
+ {\r
+ walker.range.setStartAt( body, CKEDITOR.POSITION_AFTER_START );\r
+ walker.range.setEnd( range.startContainer, range.startOffset );\r
+ previous = walker.previous();\r
+\r
+ if ( previous && previous.type == CKEDITOR.NODE_ELEMENT &&\r
+ ( previous.getName() in listNodeNames || previous.is( 'li' ) ) )\r
+ {\r
+ if ( !previous.is( 'li' ) )\r
+ {\r
+ walker.range.selectNodeContents( previous );\r
+ walker.reset();\r
+ walker.evaluator = isTextBlock;\r
+ previous = walker.previous();\r
+ }\r
+\r
+ joinWith = previous;\r
+ // Place cursor at the end of previous block.\r
+ cursor.moveToElementEditEnd( joinWith );\r
+ }\r
+ }\r
+\r
+ if ( joinWith )\r
+ {\r
+ joinNextLineToCursor( editor, cursor, range );\r
+ evt.cancel();\r
+ }\r
+ else\r
+ {\r
+ var list = path.contains( listNodeNames ), li;\r
+ // Backspace pressed at the start of list outdents the first list item. (#9129)\r
+ if ( list && range.checkBoundaryOfElement( list, CKEDITOR.START ) )\r
+ {\r
+ li = list.getFirst( nonEmpty );\r
+\r
+ if ( range.checkBoundaryOfElement( li, CKEDITOR.START ) )\r
+ {\r
+ previous = list.getPrevious( nonEmpty );\r
+\r
+ // Only if the list item contains a sub list, do nothing but\r
+ // simply move cursor backward one character.\r
+ if ( getSubList( li ) )\r
+ {\r
+ if ( previous ) {\r
+ range.moveToElementEditEnd( previous );\r
+ range.select();\r
+ }\r
+\r
+ evt.cancel();\r
+ }\r
+ else\r
+ {\r
+ editor.execCommand( 'outdent' );\r
+ evt.cancel();\r
+ }\r
+ }\r
+ }\r
+ }\r
+ }\r
+ else\r
+ {\r
+ var next, nextLine;\r
+ li = range.startContainer.getAscendant( 'li', 1 );\r
+\r
+ if ( li )\r
+ {\r
+ walker.range.setEndAt( body, CKEDITOR.POSITION_BEFORE_END );\r
+\r
+ var last = li.getLast( nonEmpty );\r
+ var block = last && isTextBlock( last ) ? last : li;\r
+\r
+ // Indicate cursor at the visual end of an list item.\r
+ var isAtEnd = 0;\r
+\r
+ next = walker.next();\r
+\r
+ // When list item contains a sub list.\r
+ if ( next && next.type == CKEDITOR.NODE_ELEMENT &&\r
+ next.getName() in listNodeNames\r
+ && next.equals( last ) )\r
+ {\r
+ isAtEnd = 1;\r
+\r
+ // Move to the first item in sub list.\r
+ next = walker.next();\r
+ }\r
+ // Right at the end of list item.\r
+ else if ( range.checkBoundaryOfElement( block, CKEDITOR.END ) )\r
+ isAtEnd = 1;\r
+\r
+\r
+ if ( isAtEnd && next )\r
+ {\r
+ // Put cursor range there.\r
+ nextLine = range.clone();\r
+ nextLine.moveToElementEditStart( next );\r
+\r
+ joinNextLineToCursor( editor, cursor, nextLine );\r
+ evt.cancel();\r
+ }\r
+ }\r
+ else\r
+ {\r
+ // Handle Del key pressed before the list.\r
+ walker.range.setEndAt( body, CKEDITOR.POSITION_BEFORE_END );\r
+ next = walker.next();\r
+\r
+ if ( next && next.type == CKEDITOR.NODE_ELEMENT &&\r
+ next.getName() in listNodeNames )\r
+ {\r
+ // The start <li>\r
+ next = next.getFirst( nonEmpty );\r
+\r
+ // Simply remove the current empty block, move cursor to the\r
+ // subsequent list.\r
+ if ( path.block &&\r
+ range.checkStartOfBlock() &&\r
+ range.checkEndOfBlock() )\r
+ {\r
+ path.block.remove();\r
+ range.moveToElementEditStart( next );\r
+ range.select();\r
+ evt.cancel();\r
+ }\r
+\r
+ // Preventing the default (merge behavior), but simply move\r
+ // the cursor one character forward if subsequent list item\r
+ // contains sub list.\r
+ else if ( getSubList( next ) )\r
+ {\r
+ range.moveToElementEditStart( next );\r
+ range.select();\r
+ evt.cancel();\r
+ }\r
+ // Merge the first list item with the current line.\r
+ else\r
+ {\r
+ nextLine = range.clone();\r
+ nextLine.moveToElementEditStart( next );\r
+ joinNextLineToCursor( editor, cursor, nextLine );\r
+ evt.cancel();\r
+ }\r
+ }\r
+ }\r
+ }\r
+\r
+ // The backspace/del could potentially put cursor at a bad position,\r
+ // being it handled or not, check immediately the selection to have it fixed.\r
+ setTimeout( function() { editor.selectionChange( 1 ); } );\r
+ }\r
+ } );\r