| 748 | // Check if node is block element that recieves text. |
| 749 | function isTextBlock( node ) |
| 750 | { |
| 751 | return node.type == CKEDITOR.NODE_ELEMENT && |
| 752 | ( node.getName() in CKEDITOR.dtd.$block || |
| 753 | node.getName() in CKEDITOR.dtd.$listItem ) && |
| 754 | CKEDITOR.dtd[ node.getName() ][ '#' ]; |
| 755 | } |
| 756 | |
| 757 | // Merge the visual line content at the cursor range into the block. |
| 758 | function joinNextLineToCursor( editor, cursor, nextCursor ) |
| 759 | { |
| 760 | editor.fire( 'saveSnapshot' ); |
| 761 | |
| 762 | // Merge with previous block's content. |
| 763 | nextCursor.enlarge( CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ); |
| 764 | var frag = nextCursor.extractContents(); |
| 765 | |
| 766 | cursor.trim( false, true ); |
| 767 | |
| 768 | // Kill original bogus; |
| 769 | var currentPath = new CKEDITOR.dom.elementPath( cursor.startContainer ); |
| 770 | var currentLi = currentPath.lastElement.getAscendant( 'li', 1 ); |
| 771 | |
| 772 | var bogus = currentPath.block.getBogus(); |
| 773 | bogus && bogus.remove(); |
| 774 | |
| 775 | // Kill the tail br in extracted. |
| 776 | var last = frag.getLast(); |
| 777 | if ( last && last.type == CKEDITOR.NODE_ELEMENT && last.is( 'br' ) ) |
| 778 | last.remove(); |
| 779 | |
| 780 | // Insert fragment at the range position. |
| 781 | var nextNode = cursor.startContainer.getChild( cursor.startOffset ); |
| 782 | if ( nextNode ) |
| 783 | frag.insertBefore( nextNode ); |
| 784 | else |
| 785 | cursor.startContainer.append( frag ); |
| 786 | |
| 787 | var nextPath = new CKEDITOR.dom.elementPath( nextCursor.startContainer ); |
| 788 | var nextLi = nextCursor.startContainer.getAscendant( 'li', 1 ); |
| 789 | |
| 790 | // Move the sub list nested in the next list item. |
| 791 | if ( nextLi ) |
| 792 | { |
| 793 | var sublist = getSubList( nextLi ); |
| 794 | if ( sublist ) |
| 795 | { |
| 796 | // If next line is in the sub list of the current list item. |
| 797 | if ( currentLi.contains( nextLi ) ) |
| 798 | { |
| 799 | mergeListItems( sublist, nextLi.getParent(), nextLi ); |
| 800 | sublist.remove(); |
| 801 | } |
| 802 | // Migrate the sub list to current list item. |
| 803 | else |
| 804 | currentLi.append( sublist ); |
| 805 | } |
| 806 | } |
| 807 | |
| 808 | |
| 809 | if ( nextCursor.checkStartOfBlock() && |
| 810 | nextCursor.checkEndOfBlock() ) |
| 811 | { |
| 812 | var nextBlock = nextPath.block, |
| 813 | parentBlock = nextBlock.getParent(); |
| 814 | |
| 815 | nextBlock.remove(); |
| 816 | |
| 817 | // Remove if the path block container is now empty, e.g. li. |
| 818 | if ( parentBlock && |
| 819 | !parentBlock.getFirst( nonEmpty ) && |
| 820 | !parentBlock.equals( nextPath.blockLimit ) ) |
| 821 | { |
| 822 | parentBlock.remove(); |
| 823 | } |
| 824 | } |
| 825 | |
| 826 | // Make fresh selection. |
| 827 | cursor.select(); |
| 828 | |
| 829 | editor.fire( 'saveSnapshot' ); |
| 830 | } |
| 831 | |
| 832 | function getSubList( li ) |
| 833 | { |
| 834 | var last = li.getLast( nonEmpty ); |
| 835 | return last && last.type == CKEDITOR.NODE_ELEMENT && last.getName() in listNodeNames ? last : null; |
| 836 | } |
| 837 | |
| 861 | |
| 862 | // [IE8] Fix "backspace" after list and "del" at the end of list item. (#8248) |
| 863 | // if ( CKEDITOR.env.ie8Compat ) |
| 864 | if ( 1 ) |
| 865 | { |
| 866 | editor.on( 'key', function( evt ) |
| 867 | { |
| 868 | var key = evt.data.keyCode; |
| 869 | |
| 870 | // DEl/BACKSPACE |
| 871 | if ( editor.mode == 'wysiwyg' && key in { 8 : 1, 46 : 1 } ) |
| 872 | { |
| 873 | var sel = editor.getSelection(), |
| 874 | range = sel.getRanges()[ 0 ]; |
| 875 | |
| 876 | if ( !range.collapsed ) |
| 877 | return; |
| 878 | |
| 879 | var isBackspace = key == 8; |
| 880 | var body = editor.document.getBody(); |
| 881 | var walker = new CKEDITOR.dom.walker( range.clone() ); |
| 882 | walker.evaluator = function( node ) { return nonEmpty( node ) && !blockBogus( node ); }; |
| 883 | |
| 884 | var cursor = range.clone(); |
| 885 | |
| 886 | if ( isBackspace ) |
| 887 | { |
| 888 | walker.range.setStartAt( body, CKEDITOR.POSITION_AFTER_START ); |
| 889 | walker.range.setEnd( range.startContainer, range.startOffset ); |
| 890 | |
| 891 | var previous = walker.previous(); |
| 892 | |
| 893 | // Check if cursor collapsed right behind of a list. |
| 894 | if ( previous && |
| 895 | previous.type == CKEDITOR.NODE_ELEMENT && |
| 896 | previous.getName() in listNodeNames ) |
| 897 | { |
| 898 | walker.range.selectNodeContents( previous ); |
| 899 | walker.reset(); |
| 900 | walker.evaluator = isTextBlock; |
| 901 | |
| 902 | // Place cursor at the end of previous block. |
| 903 | cursor.moveToElementEditEnd( walker.lastForward() ); |
| 904 | joinNextLineToCursor( editor, cursor, range ); |
| 905 | evt.cancel(); |
| 906 | } |
| 907 | } |
| 908 | else |
| 909 | { |
| 910 | var li = range.startContainer.getAscendant( 'li', 1 ); |
| 911 | if ( li ) |
| 912 | { |
| 913 | walker.range.setEndAt( body, CKEDITOR.POSITION_BEFORE_END ); |
| 914 | |
| 915 | var last = li.getLast( nonEmpty ); |
| 916 | var block = last && isTextBlock( last ) ? last : li; |
| 917 | |
| 918 | // Indicate cursor at the visual end of an list item. |
| 919 | var isAtEnd = 0; |
| 920 | |
| 921 | var next = walker.next(); |
| 922 | |
| 923 | // When list item contains a sub list. |
| 924 | if ( next && next.type == CKEDITOR.NODE_ELEMENT && |
| 925 | next.getName() in listNodeNames |
| 926 | && next.equals( last ) ) |
| 927 | { |
| 928 | isAtEnd = 1; |
| 929 | |
| 930 | // Move to the first item in sub list. |
| 931 | next = walker.next(); |
| 932 | } |
| 933 | // Right at the end of list item. |
| 934 | else if ( range.checkBoundaryOfElement( block, CKEDITOR.END ) ) |
| 935 | isAtEnd = 1; |
| 936 | |
| 937 | |
| 938 | if ( isAtEnd && next ) |
| 939 | { |
| 940 | // Put cursor range there. |
| 941 | var nextLine = range.clone(); |
| 942 | nextLine.moveToElementEditStart( next ); |
| 943 | |
| 944 | joinNextLineToCursor( editor, cursor, nextLine ); |
| 945 | evt.cancel(); |
| 946 | } |
| 947 | } |
| 948 | } |
| 949 | } |
| 950 | } ); |
| 951 | } |