]> git.immae.eu Git - perso/Immae/Projets/packagist/piedsjaloux-ckeditor-component.git/blobdiff - sources/core/editable.js
Add oembed
[perso/Immae/Projets/packagist/piedsjaloux-ckeditor-component.git] / sources / core / editable.js
index b9b0270b1443b52a0b6424127022eebad3b2ba28..6b3fa9f7d7e64c4454d9a02e3e8fb3009b912bf3 100644 (file)
@@ -1,9 +1,12 @@
 /**
- * @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved.
+ * @license Copyright (c) 2003-2017, CKSource - Frederico Knabben. All rights reserved.
  * For licensing, see LICENSE.md or http://ckeditor.com/license
  */
 
 ( function() {
+       var isNotWhitespace, isNotBookmark, isEmpty, isBogus, emptyParagraphRegexp,
+               insert, fixTableAfterContentsDeletion, fixListAfterContentsDelete, getHtmlFromRangeHelpers, extractHtmlFromRangeHelpers;
+
        /**
         * Editable class which provides all editing related activities by
         * the `contenteditable` element, dynamically get attached to editor instance.
                                        }
                                }
 
-                               // [IE] Use instead "setActive" method to focus the editable if it belongs to
-                               // the host page document, to avoid bringing an unexpected scroll.
+                               // [Edge] Starting from EdgeHTML 14.14393, it does not support `setActive`. We need to use focus which
+                               // causes unexpected scroll. Store scrollTop value so it can be restored after focusing editor.
+                               // Scroll only happens if the editor is focused for the first time. (http://dev.ckeditor.com/ticket/14825)
+                               if ( CKEDITOR.env.edge && CKEDITOR.env.version > 14 && !this.hasFocus && this.getDocument().equals( CKEDITOR.document ) ) {
+                                       this.editor._.previousScrollTop = this.$.scrollTop;
+                               }
+
+                               // [IE] Use instead "setActive" method to focus the editable if it belongs to the host page document,
+                               // to avoid bringing an unexpected scroll.
                                try {
-                                       this.$[ CKEDITOR.env.ie && this.getDocument().equals( CKEDITOR.document ) ? 'setActive' : 'focus' ]();
+                                       if ( CKEDITOR.env.ie && !( CKEDITOR.env.edge && CKEDITOR.env.version > 14 ) && this.getDocument().equals( CKEDITOR.document ) ) {
+                                               this.$.setActive();
+                                       } else {
+                                               // We have no control over exactly what happens when the native `focus` method is called,
+                                               // so save the scroll position and restore it later.
+                                               if ( CKEDITOR.env.chrome ) {
+                                                       var scrollPos = this.$.scrollTop;
+                                                       this.$.focus();
+                                                       this.$.scrollTop = scrollPos;
+                                               } else {
+                                                       this.$.focus();
+                                               }
+                                       }
                                } catch ( e ) {
                                        // IE throws unspecified error when focusing editable after closing dialog opened on nested editable.
                                        if ( !CKEDITOR.env.ie )
                                                throw e;
                                }
 
-                               // Remedy if Safari doens't applies focus properly. (#279)
+                               // Remedy if Safari doens't applies focus properly. (http://dev.ckeditor.com/ticket/279)
                                if ( CKEDITOR.env.safari && !this.isInline() ) {
                                        active = CKEDITOR.document.getActive();
                                        if ( !active.equals( this.getWindow().getFrame() ) )
 
                                        // The "focusin/focusout" events bubbled, e.g. If there are elements with layout
                                        // they fire this event when clicking in to edit them but it must be ignored
-                                       // to allow edit their contents. (#4682)
+                                       // to allow edit their contents. (http://dev.ckeditor.com/ticket/4682)
                                        fn = isNotBubbling( fn, this );
                                        args[ 0 ] = name;
                                        args[ 1 ] = fn;
                         * @param {String} text
                         */
                        insertText: function( text ) {
-                               // Focus the editor before calling transformPlainTextToHtml. (#12726)
+                               // Focus the editor before calling transformPlainTextToHtml. (http://dev.ckeditor.com/ticket/12726)
                                this.editor.focus();
                                this.insertHtml( this.transformPlainTextToHtml( text ), 'text' );
                        },
                        insertElement: function( element, range ) {
                                var editor = this.editor;
 
-                               // Prepare for the insertion. For example - focus editor (#11848).
+                               // Prepare for the insertion. For example - focus editor (http://dev.ckeditor.com/ticket/11848).
                                editor.focus();
                                editor.fire( 'saveSnapshot' );
 
                                        range = selection.getRanges()[ 0 ];
                                }
 
-                               // Insert element into first range only and ignore the rest (#11183).
+                               // Insert element into first range only and ignore the rest (http://dev.ckeditor.com/ticket/11183).
                                if ( this.insertElementIntoRange( element, range ) ) {
                                        range.moveToPosition( element, CKEDITOR.POSITION_AFTER_END );
 
                                        // If we're inserting a block element, the new cursor position must be
-                                       // optimized. (#3100,#5436,#8950)
+                                       // optimized. (http://dev.ckeditor.com/ticket/3100,http://dev.ckeditor.com/ticket/5436,http://dev.ckeditor.com/ticket/8950)
                                        if ( isBlock ) {
                                                // Find next, meaningful element.
                                                var next = element.getNext( function( node ) {
                                // Remove the original contents, merge split nodes.
                                range.deleteContents( 1 );
 
-                               // If range is placed in inermediate element (not td or th), we need to do three things:
-                               // * fill emptied <td/th>s with if browser needs them,
-                               // * remove empty text nodes so IE8 won't crash (http://dev.ckeditor.com/ticket/11183#comment:8),
-                               // * fix structure and move range into the <td/th> element.
-                               if ( range.startContainer.type == CKEDITOR.NODE_ELEMENT && range.startContainer.is( { tr: 1, table: 1, tbody: 1, thead: 1, tfoot: 1 } ) )
-                                       fixTableAfterContentsDeletion( range );
+                               if ( range.startContainer.type == CKEDITOR.NODE_ELEMENT ) {
+                                       // If range is placed in intermediate element (not td or th), we need to do three things:
+                                       // * fill emptied <td/th>s with if browser needs them,
+                                       // * remove empty text nodes so IE8 won't crash
+                                       // (http://dev.ckeditor.com/ticket/11183#comment:8),
+                                       // * fix structure and move range into the <td/th> element.
+                                       if ( range.startContainer.is( { tr: 1, table: 1, tbody: 1, thead: 1, tfoot: 1 } ) ) {
+                                               fixTableAfterContentsDeletion( range );
+                                       } else if ( range.startContainer.is( CKEDITOR.dtd.$list ) ) {
+                                               // Similarly there's a need for lists.
+                                               fixListAfterContentsDelete( range );
+                                       }
+                               }
 
                                // If we're inserting a block at dtd-violated position, split
                                // the parent blocks until we reach blockLimit.
                                                        range.splitElement( current );
 
                                                // If we're in an empty block which indicate a new paragraph,
-                                               // simply replace it with the inserting block.(#3664)
+                                               // simply replace it with the inserting block.(http://dev.ckeditor.com/ticket/3664)
                                                else if ( range.checkStartOfBlock() && range.checkEndOfBlock() ) {
                                                        range.setStartBefore( current );
                                                        range.collapse( true );
                                                range.checkEndOfBlock() &&
                                                path.block &&
                                                !range.root.equals( path.block ) &&
-                                               // Do not remove a block with bookmarks. (#13465)
+                                               // Do not remove a block with bookmarks. (http://dev.ckeditor.com/ticket/13465)
                                                !hasBookmarks( path.block ) ) {
                                                range.moveToPosition( path.block, CKEDITOR.POSITION_BEFORE_START );
                                                path.block.remove();
 
                                        // IE considers control-type element as separate
                                        // focus host when selected, avoid destroying the
-                                       // selection in such case. (#5812) (#8949)
+                                       // selection in such case. (http://dev.ckeditor.com/ticket/5812) (http://dev.ckeditor.com/ticket/8949)
                                        if ( ieSel && ieSel.type == 'Control' )
                                                return;
 
                                        this.hasFocus = true;
                                }, null, null, -1 );
 
+                               if ( CKEDITOR.env.webkit ) {
+                                       // [WebKit] Save scrollTop value so it can be used when restoring locked selection. (http://dev.ckeditor.com/ticket/14659)
+                                       this.on( 'scroll', function() {
+                                               editor._.previousScrollTop = editor.editable().$.scrollTop;
+                                       }, null, null, -1 );
+                               }
+
+                               // [Edge] This is the other part of the workaround for Edge which restores saved
+                               // scrollTop value and removes listener which is not needed anymore. (http://dev.ckeditor.com/ticket/14825)
+                               if ( CKEDITOR.env.edge && CKEDITOR.env.version > 14 ) {
+
+                                       var fixScrollOnFocus = function() {
+                                               var editable = editor.editable();
+
+                                               if ( editor._.previousScrollTop != null && editable.getDocument().equals( CKEDITOR.document ) ) {
+                                                       editable.$.scrollTop = editor._.previousScrollTop;
+                                                       editor._.previousScrollTop = null;
+                                                       this.removeListener( 'scroll', fixScrollOnFocus );
+                                               }
+                                       };
+
+                                       this.on( 'scroll', fixScrollOnFocus );
+                               }
+
                                // Register to focus manager.
                                editor.focusManager.add( this );
 
                                // Create the content stylesheet for this document.
                                var styles = CKEDITOR.getCss();
                                if ( styles ) {
-                                       var head = doc.getHead();
-                                       if ( !head.getCustomData( 'stylesheet' ) ) {
+                                       var head = doc.getHead(),
+                                               stylesElement = head.getCustomData( 'stylesheet' );
+
+                                       if ( !stylesElement ) {
                                                var sheet = doc.appendStyleText( styles );
                                                sheet = new CKEDITOR.dom.element( sheet.ownerNode || sheet.owningElement );
                                                head.setCustomData( 'stylesheet', sheet );
                                                sheet.data( 'cke-temp', 1 );
+                                       } else if ( styles != stylesElement.getText() ) {
+                                               CKEDITOR.env.ie && CKEDITOR.env.version < 9 ? stylesElement.$.styleSheet.cssText = styles : stylesElement.setText( styles );
                                        }
                                }
 
                                // Pass this configuration to styles system.
                                this.setCustomData( 'cke_includeReadonly', !editor.config.disableReadonlyStyling );
 
-                               // Prevent the browser opening read-only links. (#6032 & #10912)
+                               // Prevent the browser opening read-only links. (http://dev.ckeditor.com/ticket/6032 & http://dev.ckeditor.com/ticket/10912)
                                this.attachListener( this, 'click', function( evt ) {
                                        evt = evt.data;
 
                                var backspaceOrDelete = { 8: 1, 46: 1 };
 
                                // Override keystrokes which should have deletion behavior
-                               //  on fully selected element . (#4047) (#7645)
+                               //  on fully selected element . (http://dev.ckeditor.com/ticket/4047) (http://dev.ckeditor.com/ticket/7645)
                                this.attachListener( editor, 'key', function( evt ) {
                                        if ( editor.readOnly )
                                                return true;
                                        var keyCode = evt.data.domEvent.getKey(),
                                                isHandled;
 
+                                       // Prevent of reading path of empty range (http://dev.ckeditor.com/ticket/13096, #457).
+                                       var sel = editor.getSelection();
+                                       if ( sel.getRanges().length === 0 ) {
+                                               return;
+                                       }
+
                                        // Backspace OR Delete.
                                        if ( keyCode in backspaceOrDelete ) {
-                                               var sel = editor.getSelection(),
-                                                       selected,
+                                               var selected,
                                                        range = sel.getRanges()[ 0 ],
                                                        path = range.startPath(),
                                                        block,
                                                        next,
                                                        rtl = keyCode == 8;
 
+
                                                if (
-                                                               // [IE<11] Remove selected image/anchor/etc here to avoid going back in history. (#10055)
+                                                               // [IE<11] Remove selected image/anchor/etc here to avoid going back in history. (http://dev.ckeditor.com/ticket/10055)
                                                                ( CKEDITOR.env.ie && CKEDITOR.env.version < 11 && ( selected = sel.getSelectedElement() ) ) ||
-                                                               // Remove the entire list/table on fully selected content. (#7645)
+                                                               // Remove the entire list/table on fully selected content. (http://dev.ckeditor.com/ticket/7645)
                                                                ( selected = getSelectedTableList( sel ) ) ) {
                                                        // Make undo snapshot.
                                                        editor.fire( 'saveSnapshot' );
 
                                                        // Delete any element that 'hasLayout' (e.g. hr,table) in IE8 will
-                                                       // break up the selection, safely manage it here. (#4795)
+                                                       // break up the selection, safely manage it here. (http://dev.ckeditor.com/ticket/4795)
                                                        range.moveToPosition( selected, CKEDITOR.POSITION_BEFORE_START );
                                                        // Remove the control manually.
                                                        selected.remove();
 
                                                        isHandled = 1;
                                                } else if ( range.collapsed ) {
-                                                       // Handle the following special cases: (#6217)
+                                                       // Handle the following special cases: (http://dev.ckeditor.com/ticket/6217)
                                                        // 1. Del/Backspace key before/after table;
                                                        // 2. Backspace Key after start of table.
                                                        if ( ( block = path.block ) &&
                                        editor.fire( 'doubleclick', data );
                                } );
 
-                               // Prevent automatic submission in IE #6336
+                               // Prevent automatic submission in IE http://dev.ckeditor.com/ticket/6336
                                CKEDITOR.env.ie && this.attachListener( this, 'click', blockInputClick );
 
-                               // Gecko/Webkit need some help when selecting control type elements. (#3448)
-                               // We apply same behavior for IE Edge. (#13386)
+                               // Gecko/Webkit need some help when selecting control type elements. (http://dev.ckeditor.com/ticket/3448)
+                               // We apply same behavior for IE Edge. (http://dev.ckeditor.com/ticket/13386)
                                if ( !CKEDITOR.env.ie || CKEDITOR.env.edge ) {
                                        this.attachListener( this, 'mousedown', function( ev ) {
                                                var control = ev.data.getTarget();
-                                               // #11727. Note: htmlDP assures that input/textarea/select have contenteditable=false
+                                               // http://dev.ckeditor.com/ticket/11727. Note: htmlDP assures that input/textarea/select have contenteditable=false
                                                // attributes. However, they also have data-cke-editable attribute, so isReadOnly() returns false,
                                                // and therefore those elements are correctly selected by this code.
                                                if ( control.is( 'img', 'hr', 'input', 'textarea', 'select' ) && !control.isReadOnly() ) {
                                                        editor.getSelection().selectElement( control );
 
-                                                       // Prevent focus from stealing from the editable. (#9515)
+                                                       // Prevent focus from stealing from the editable. (http://dev.ckeditor.com/ticket/9515)
                                                        if ( control.is( 'input', 'textarea', 'select' ) )
                                                                ev.data.preventDefault();
                                                }
                                        } );
                                }
 
-                               // For some reason, after click event is done, IE Edge loses focus on the selected element. (#13386)
+                               // For some reason, after click event is done, IE Edge loses focus on the selected element. (http://dev.ckeditor.com/ticket/13386)
                                if ( CKEDITOR.env.edge ) {
                                        this.attachListener( this, 'mouseup', function( ev ) {
                                                var selectedElement = ev.data.getTarget();
                                }
 
                                // Prevent right click from selecting an empty block even
-                               // when selection is anchored inside it. (#5845)
+                               // when selection is anchored inside it. (http://dev.ckeditor.com/ticket/5845)
                                if ( CKEDITOR.env.gecko ) {
                                        this.attachListener( this, 'mouseup', function( ev ) {
                                                if ( ev.data.$.button == 2 ) {
                                }
 
                                // Prevent Webkit/Blink from going rogue when joining
-                               // blocks on BACKSPACE/DEL (#11861,#9998).
+                               // blocks on BACKSPACE/DEL (http://dev.ckeditor.com/ticket/11861,http://dev.ckeditor.com/ticket/9998).
                                if ( CKEDITOR.env.webkit ) {
                                        this.attachListener( editor, 'key', function( evt ) {
                                                if ( editor.readOnly ) {
                                                if ( !( key in backspaceOrDelete ) )
                                                        return;
 
+                                               // Prevent of reading path of empty range (http://dev.ckeditor.com/ticket/13096, #457).
+                                               var sel = editor.getSelection();
+                                               if ( sel.getRanges().length === 0 ) {
+                                                       return;
+                                               }
+
                                                var backspace = key == 8,
-                                                       range = editor.getSelection().getRanges()[ 0 ],
+                                                       range = sel.getRanges()[ 0 ],
                                                        startPath = range.startPath();
 
                                                if ( range.collapsed ) {
                                                                return;
                                                }
 
-                                               // Scroll to the new position of the caret (#11960).
+                                               // Scroll to the new position of the caret (http://dev.ckeditor.com/ticket/11960).
                                                editor.getSelection().scrollIntoView();
                                                editor.fire( 'saveSnapshot' );
 
         *
         * @method editable
         * @member CKEDITOR.editor
-        * @param {CKEDITOR.dom.element/CKEDITOR.editable} elementOrEditable The
+        * @param {CKEDITOR.dom.element/CKEDITOR.editable} [elementOrEditable] The
         * DOM element to become the editable or a {@link CKEDITOR.editable} object.
+        * @returns {CKEDITOR.dom.element/null} The editor's editable element, or `null` if not available.
         */
        CKEDITOR.editor.prototype.editable = function( element ) {
                var editable = this._.editable;
        CKEDITOR.on( 'instanceLoaded', function( evt ) {
                var editor = evt.editor;
 
-               // and flag that the element was locked by our code so it'll be editable by the editor functions (#6046).
+               // and flag that the element was locked by our code so it'll be editable by the editor functions (http://dev.ckeditor.com/ticket/6046).
                editor.on( 'insertElement', function( evt ) {
                        var element = evt.data;
                        if ( element.type == CKEDITOR.NODE_ELEMENT && ( element.is( 'input' ) || element.is( 'textarea' ) ) ) {
                        if ( editor.readOnly )
                                return;
 
-                       // Auto fixing on some document structure weakness to enhance usabilities. (#3190 and #3189)
+                       // Auto fixing on some document structure weakness to enhance usabilities. (http://dev.ckeditor.com/ticket/3190 and http://dev.ckeditor.com/ticket/3189)
                        var sel = editor.getSelection();
-                       // Do it only when selection is not locked. (#8222)
+                       // Do it only when selection is not locked. (http://dev.ckeditor.com/ticket/8222)
                        if ( sel && !sel.isLocked ) {
                                var isDirty = editor.checkDirty();
 
                } );
        } );
 
-       // #9222: Show text cursor in Gecko.
+       // http://dev.ckeditor.com/ticket/9222: Show text cursor in Gecko.
        // Show default cursor over control elements on all non-IEs.
        CKEDITOR.addCss( '.cke_editable{cursor:text}.cke_editable img,.cke_editable input,.cke_editable textarea{cursor:default}' );
 
        //
        //
 
-       var isNotWhitespace = CKEDITOR.dom.walker.whitespaces( true ),
-               isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true ),
-               isEmpty = CKEDITOR.dom.walker.empty(),
-               isBogus = CKEDITOR.dom.walker.bogus(),
-               // Matching an empty paragraph at the end of document.
-               emptyParagraphRegexp = /(^|<body\b[^>]*>)\s*<(p|div|address|h\d|center|pre)[^>]*>\s*(?:<br[^>]*>|&nbsp;|\u00A0|&#160;)?\s*(:?<\/\2>)?\s*(?=$|<\/body>)/gi;
+       isNotWhitespace = CKEDITOR.dom.walker.whitespaces( true ),
+       isNotBookmark = CKEDITOR.dom.walker.bookmark( false, true ),
+       isEmpty = CKEDITOR.dom.walker.empty(),
+       isBogus = CKEDITOR.dom.walker.bogus(),
+       // Matching an empty paragraph at the end of document.
+       emptyParagraphRegexp = /(^|<body\b[^>]*>)\s*<(p|div|address|h\d|center|pre)[^>]*>\s*(?:<br[^>]*>|&nbsp;|\u00A0|&#160;)?\s*(:?<\/\2>)?\s*(?=$|<\/body>)/gi;
 
-       // Auto-fixing block-less content by wrapping paragraph (#3190), prevent
-       // non-exitable-block by padding extra br.(#3189)
+       // Auto-fixing block-less content by wrapping paragraph (http://dev.ckeditor.com/ticket/3190), prevent
+       // non-exitable-block by padding extra br.(http://dev.ckeditor.com/ticket/3189)
        // Returns truly value when dom was changed, falsy otherwise.
        function fixDom( evt ) {
                var editor = evt.editor,
                }
 
                // When we're in block enter mode, a new paragraph will be established
-               // to encapsulate inline contents inside editable. (#3657)
+               // to encapsulate inline contents inside editable. (http://dev.ckeditor.com/ticket/3657)
                // Don't autoparagraph if browser (namely - IE) incorrectly anchored selection
                // inside non-editable content. This happens e.g. if non-editable block is the only
                // content of editable.
 
                                selectionUpdateNeeded = 1;
 
-                               // Cancel this selection change in favor of the next (correct). (#6811)
+                               // Cancel this selection change in favor of the next (correct). (http://dev.ckeditor.com/ticket/6811)
                                evt.cancel();
                        }
                }
                if ( selection.isFake )
                        return 0;
 
-               // Ensure bogus br could help to move cursor (out of styles) to the end of block. (#7041)
+               // Ensure bogus br could help to move cursor (out of styles) to the end of block. (http://dev.ckeditor.com/ticket/7041)
                var pathBlock = path.block || path.blockLimit,
                        lastNode = pathBlock && pathBlock.getLast( isNotEmpty );
 
                // Check some specialities of the current path block:
-               // 1. It is really displayed as block; (#7221)
-               // 2. It doesn't end with one inner block; (#7467)
+               // 1. It is really displayed as block; (http://dev.ckeditor.com/ticket/7221)
+               // 2. It doesn't end with one inner block; (http://dev.ckeditor.com/ticket/7467)
                // 3. It doesn't have bogus br yet.
                if (
                        pathBlock && pathBlock.isBlockBoundary() &&
        // Whether in given context (pathBlock, pathBlockLimit and editor settings)
        // editor should automatically wrap inline contents with blocks.
        function shouldAutoParagraph( editor, pathBlock, pathBlockLimit ) {
-               // Check whether pathBlock equals pathBlockLimit to support nested editable (#12162).
+               // Check whether pathBlock equals pathBlockLimit to support nested editable (http://dev.ckeditor.com/ticket/12162).
                return editor.config.autoParagraph !== false &&
                        editor.activeEnterMode != CKEDITOR.ENTER_BR &&
                        (
        //
        // Functions related to insertXXX methods
        //
-       var insert = ( function() {
+       insert = ( function() {
                'use strict';
 
                var DTD = CKEDITOR.dtd;
 
                        // Select range and stop execution.
                        // If data has been totally emptied after the filtering,
-                       // any insertion is pointless (#10339).
+                       // any insertion is pointless (http://dev.ckeditor.com/ticket/10339).
                        if ( data && processDataForInsertion( that, data ) ) {
                                // DATA INSERTION
                                insertDataIntoRange( that );
                                        nodeName = node.getName();
 
                                        // Extract only the list items, when insertion happens
-                                       // inside of a list, reads as rearrange list items. (#7957)
+                                       // inside of a list, reads as rearrange list items. (http://dev.ckeditor.com/ticket/7957)
                                        if ( insideOfList && nodeName in CKEDITOR.dtd.$list ) {
                                                nodesData = nodesData.concat( extractNodesData( node, that ) );
                                                continue;
                        }
 
                        // Don't use String.replace because it fails in IE7 if special replacement
-                       // characters ($$, $&, etc.) are in data (#10367).
+                       // characters ($$, $&, etc.) are in data (http://dev.ckeditor.com/ticket/10367).
                        return wrapper.getOuterHtml().split( '{cke-peak}' ).join( data );
                }
 
        // 1. Fixes a range which is a result of deleteContents() and is placed in an intermediate element (see dtd.$intermediate),
        // inside a table. A goal is to find a closest <td> or <th> element and when this fails, recreate the structure of the table.
        // 2. Fixes empty cells by appending bogus <br>s or deleting empty text nodes in IE<=8 case.
-       var fixTableAfterContentsDeletion = ( function() {
+       fixTableAfterContentsDeletion = ( function() {
                // Creates an element walker which can only "go deeper". It won't
                // move out from any element. Therefore it can be used to find <td>x</td> in cases like:
                // <table><tbody><tr><td>x</td></tr></tbody>^<tfoot>...
                };
        } )();
 
+       fixListAfterContentsDelete = ( function() {
+               // Creates an element walker which operates only within lists.
+               function getFixListSelectionWalker( testRange ) {
+                       var walker = new CKEDITOR.dom.walker( testRange );
+                       walker.guard = function( node, isMovingOut ) {
+                               if ( isMovingOut )
+                                       return false;
+                               if ( node.type == CKEDITOR.NODE_ELEMENT )
+                                       return node.is( CKEDITOR.dtd.$list ) || node.is( CKEDITOR.dtd.$listItem );
+                       };
+                       walker.evaluator = function( node ) {
+                               return node.type == CKEDITOR.NODE_ELEMENT && node.is( CKEDITOR.dtd.$listItem );
+                       };
+
+                       return walker;
+               }
+
+               return function( range ) {
+                       var container = range.startContainer,
+                               appendToStart = false,
+                               testRange,
+                               deeperSibling;
+
+                       // Look left.
+                       testRange = range.clone();
+                       testRange.setStart( container, 0 );
+                       deeperSibling = getFixListSelectionWalker( testRange ).lastBackward();
+
+                       // If left is empty, look right.
+                       if ( !deeperSibling ) {
+                               testRange = range.clone();
+                               testRange.setEndAt( container, CKEDITOR.POSITION_BEFORE_END );
+                               deeperSibling = getFixListSelectionWalker( testRange ).lastForward();
+                               appendToStart = true;
+                       }
+
+                       // If there's no deeper nested element in both direction - container is empty - we'll use it then.
+                       if ( !deeperSibling )
+                               deeperSibling = container;
+
+                       // We found a list what means that it's empty - remove it completely.
+                       if ( deeperSibling.is( CKEDITOR.dtd.$list ) ) {
+                               range.setStartAt( deeperSibling, CKEDITOR.POSITION_BEFORE_START );
+                               range.collapse( true );
+                               deeperSibling.remove();
+                               return;
+                       }
+
+                       // To avoid setting selection after bogus, remove it from the target list item.
+                       // We can safely do that, because we'll insert element into that cell.
+                       var bogus = deeperSibling.getBogus();
+                       if ( bogus )
+                               bogus.remove();
+
+                       range.moveToPosition( deeperSibling, appendToStart ? CKEDITOR.POSITION_AFTER_START : CKEDITOR.POSITION_BEFORE_END );
+                       range.select();
+               };
+       } )();
+
        function mergeBlocksCollapsedSelection( editor, range, backspace, startPath ) {
                var startBlock = startPath.block;
 
                if ( ( bogus = startBlock.getBogus() ) )
                        bogus.remove();
 
-               // Changing end container to element from text node (#12503).
+               // Changing end container to element from text node (http://dev.ckeditor.com/ticket/12503).
                range.enlarge( CKEDITOR.ENLARGE_INLINE );
 
                // Delete range contents. Do NOT merge. Merging is weird.
                range = editor.getSelection().getRanges()[ 0 ];
                range.collapse( 1 );
 
-               // Optimizing range containers from text nodes to elements (#12503).
+               // Optimizing range containers from text nodes to elements (http://dev.ckeditor.com/ticket/12503).
                range.optimize();
                if ( range.startContainer.getHtml() === '' ) {
                        range.startContainer.appendBogus();
        //
        // Helpers for editable.getHtmlFromRange.
        //
-       var getHtmlFromRangeHelpers = {
+       getHtmlFromRangeHelpers = {
                eol: {
                        detect: function( that, editable ) {
                                var range = that.range,
        //
        // Helpers for editable.extractHtmlFromRange.
        //
-       var extractHtmlFromRangeHelpers = ( function() {
+       extractHtmlFromRangeHelpers = ( function() {
                function optimizeBookmarkNode( node, toStart ) {
                        var parent = node.getParent();
 
                        while ( ( next = endBookmark.getNext() ) ) {
                                next.insertAfter( startBookmark );
 
-                               // Update startBookmark after insertion to avoid the reversal of nodes (#13449).
+                               // Update startBookmark after insertion to avoid the reversal of nodes (http://dev.ckeditor.com/ticket/13449).
                                startBookmark = next;
                        }
 
 
                                walker.guard = function( node, leaving ) {
                                        // Guard may be executed on some node boundaries multiple times,
-                                       // what results in creating more than one range for each selected cell. (#12964)
+                                       // what results in creating more than one range for each selected cell. (http://dev.ckeditor.com/ticket/12964)
                                        if ( node.type == CKEDITOR.NODE_ELEMENT ) {
                                                var key = 'visited_' + ( leaving ? 'out' : 'in' );
                                                if ( node.getCustomData( key ) ) {