]> git.immae.eu Git - perso/Immae/Projets/packagist/piedsjaloux-ckeditor-component.git/blobdiff - sources/core/dom/range.js
Add oembed
[perso/Immae/Projets/packagist/piedsjaloux-ckeditor-component.git] / sources / core / dom / range.js
index b5e8736302e37e30994752caaefe0bba28b135e3..742c24c6586010c06370b67099d12cf8515b4a20 100644 (file)
@@ -1,5 +1,5 @@
 /**
- * @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
  */
 
@@ -204,9 +204,14 @@ CKEDITOR.dom.range = function( root ) {
                // This allows us to not think about startNode == endNode case later on.
                // We do that only when cloning, because in other cases we can safely split this text node
                // and hence we can easily handle this case as many others.
-               if ( isClone && endNode.type == CKEDITOR.NODE_TEXT && startNode.equals( endNode ) ) {
-                       startNode = range.document.createText( startNode.substring( startOffset, endOffset ) );
-                       docFrag.append( startNode );
+
+               // We need to handle situation when selection startNode is type of NODE_ELEMENT (#426).
+               if ( isClone &&
+                       endNode.type == CKEDITOR.NODE_TEXT &&
+                       ( startNode.equals( endNode ) || ( startNode.type === CKEDITOR.NODE_ELEMENT && startNode.getFirst().equals( endNode ) ) ) ) {
+
+                       // Here we should always be inside one text node.
+                       docFrag.append( range.document.createText( endNode.substring( startOffset, endOffset ) ) );
                        return;
                }
 
@@ -214,7 +219,7 @@ CKEDITOR.dom.range = function( root ) {
                // second part. The removal will be handled by the rest of the code.
                if ( endNode.type == CKEDITOR.NODE_TEXT ) {
                        // If Extract or Delete we can split the text node,
-                       // but if Clone (2), then we cannot modify the DOM (#11586) so we mark the text node for cloning.
+                       // but if Clone (2), then we cannot modify the DOM (http://dev.ckeditor.com/ticket/11586) so we mark the text node for cloning.
                        if ( !isClone ) {
                                endNode = endNode.split( endOffset );
                        } else {
@@ -243,7 +248,7 @@ CKEDITOR.dom.range = function( root ) {
                // be handled by the rest of the code .
                if ( startNode.type == CKEDITOR.NODE_TEXT ) {
                        // If Extract or Delete we can split the text node,
-                       // but if Clone (2), then we cannot modify the DOM (#11586) so we mark
+                       // but if Clone (2), then we cannot modify the DOM (http://dev.ckeditor.com/ticket/11586) so we mark
                        // the text node for cloning.
                        if ( !isClone ) {
                                startNode.split( startOffset );
@@ -373,7 +378,7 @@ CKEDITOR.dom.range = function( root ) {
                                // If we don't do that, in next iterations nodes will be appended to wrong parent.
                                //
                                // We can just take first child because the algorithm guarantees
-                               // that this will be the only child on this level. (#13568)
+                               // that this will be the only child on this level. (http://dev.ckeditor.com/ticket/13568)
                                levelParent = levelParent.getChild( 0 );
                        }
                }
@@ -405,7 +410,7 @@ CKEDITOR.dom.range = function( root ) {
 
                                // When Extracting, move the removed node to the docFrag.
                                if ( isExtract ) {
-                                       newParent.append( node );
+                                       newParent.append( node, toStart );
                                }
                        }
 
@@ -574,7 +579,7 @@ CKEDITOR.dom.range = function( root ) {
                        // Tolerant bogus br when checking at the end of block.
                        // Reject any text node unless it's being bookmark
                        // OR it's spaces.
-                       // Reject any element unless it's being invisible empty. (#3883)
+                       // Reject any element unless it's being invisible empty. (http://dev.ckeditor.com/ticket/3883)
                        return !checkStart && isBogus( node ) ||
                                node.type == CKEDITOR.NODE_ELEMENT &&
                                node.is( CKEDITOR.dtd.$removeEmpty );
@@ -1052,7 +1057,7 @@ CKEDITOR.dom.range = function( root ) {
                        }
 
                        // Sometimes the endNode will come right before startNode for collapsed
-                       // ranges. Fix it. (#3780)
+                       // ranges. Fix it. (http://dev.ckeditor.com/ticket/3780)
                        if ( startNode.getPosition( endNode ) & CKEDITOR.POSITION_FOLLOWING )
                                startNode = endNode;
 
@@ -1201,7 +1206,8 @@ CKEDITOR.dom.range = function( root ) {
                /**
                 * Expands the range so that partial units are completely contained.
                 *
-                * @param unit {Number} The unit type to expand with.
+                * @param {Number} unit The unit type to expand with. Use one of following values: {@link CKEDITOR#ENLARGE_BLOCK_CONTENTS},
+                * {@link CKEDITOR#ENLARGE_ELEMENT}, {@link CKEDITOR#ENLARGE_INLINE}, {@link CKEDITOR#ENLARGE_LIST_ITEM_CONTENTS}.
                 * @param {Boolean} [excludeBrs=false] Whether include line-breaks when expanding.
                 */
                enlarge: function( unit, excludeBrs ) {
@@ -1323,13 +1329,13 @@ CKEDITOR.dom.range = function( root ) {
 
                                                                isWhiteSpace = /[\s\ufeff]$/.test( siblingText );
                                                        } else {
-                                                               // #12221 (Chrome) plus #11111 (Safari).
+                                                               // http://dev.ckeditor.com/ticket/12221 (Chrome) plus http://dev.ckeditor.com/ticket/11111 (Safari).
                                                                var offsetWidth0 = CKEDITOR.env.webkit ? 1 : 0;
 
                                                                // If this is a visible element.
                                                                // We need to check for the bookmark attribute because IE insists on
-                                                               // rendering the display:none nodes we use for bookmarks. (#3363)
-                                                               // Line-breaks (br) are rendered with zero width, which we don't want to include. (#7041)
+                                                               // rendering the display:none nodes we use for bookmarks. (http://dev.ckeditor.com/ticket/3363)
+                                                               // Line-breaks (br) are rendered with zero width, which we don't want to include. (http://dev.ckeditor.com/ticket/7041)
                                                                if ( ( sibling.$.offsetWidth > offsetWidth0 || excludeBrs && sibling.is( 'br' ) ) && !sibling.data( 'cke-bookmark' ) ) {
                                                                        // We'll accept it only if we need
                                                                        // whitespace, and this is an inline
@@ -1398,7 +1404,7 @@ CKEDITOR.dom.range = function( root ) {
 
                                        // Process the end boundary. This is basically the same
                                        // code used for the start boundary, with small changes to
-                                       // make it work in the oposite side (to the right). This
+                                       // make it work in the opposite side (to the right). This
                                        // makes it difficult to reuse the code here. So, fixes to
                                        // the above code are likely to be replicated here.
 
@@ -1481,7 +1487,7 @@ CKEDITOR.dom.range = function( root ) {
                                                        }
                                                }
                                        } else {
-                                               // Get the node right after the boudary to be checked
+                                               // Get the node right after the boundary to be checked
                                                // first.
                                                sibling = container.getChild( offset );
 
@@ -1524,8 +1530,8 @@ CKEDITOR.dom.range = function( root ) {
                                                        } else if ( sibling.type == CKEDITOR.NODE_ELEMENT ) {
                                                                // If this is a visible element.
                                                                // We need to check for the bookmark attribute because IE insists on
-                                                               // rendering the display:none nodes we use for bookmarks. (#3363)
-                                                               // Line-breaks (br) are rendered with zero width, which we don't want to include. (#7041)
+                                                               // rendering the display:none nodes we use for bookmarks. (http://dev.ckeditor.com/ticket/3363)
+                                                               // Line-breaks (br) are rendered with zero width, which we don't want to include. (http://dev.ckeditor.com/ticket/7041)
                                                                if ( ( sibling.$.offsetWidth > 0 || excludeBrs && sibling.is( 'br' ) ) && !sibling.data( 'cke-bookmark' ) ) {
                                                                        // We'll accept it only if we need
                                                                        // whitespace, and this is an inline
@@ -1616,7 +1622,7 @@ CKEDITOR.dom.range = function( root ) {
                                                boundaryGuard = function( node ) {
                                                        // We should not check contents of non-editable elements. It may happen
                                                        // that inline widget has display:table child which should not block range#enlarge.
-                                                       // When encoutered non-editable element...
+                                                       // When encountered non-editable element...
                                                        if ( node.type == CKEDITOR.NODE_ELEMENT && node.getAttribute( 'contenteditable' ) == 'false' ) {
                                                                if ( inNonEditable ) {
                                                                        // ... in which we already were, reset it (because we're leaving it) and return.
@@ -1638,7 +1644,7 @@ CKEDITOR.dom.range = function( root ) {
                                                                blockBoundary = node;
                                                        return retval;
                                                },
-                                               // Record the encounted 'tailBr' for later use.
+                                               // Record the encountered 'tailBr' for later use.
                                                tailBrGuard = function( node ) {
                                                        var retval = boundaryGuard( node );
                                                        if ( !retval && node.is && node.is( 'br' ) )
@@ -1659,7 +1665,7 @@ CKEDITOR.dom.range = function( root ) {
                                        this.setStartAt( blockBoundary, !blockBoundary.is( 'br' ) && ( !enlargeable && this.checkStartOfBlock() ||
                                                enlargeable && blockBoundary.contains( enlargeable ) ) ? CKEDITOR.POSITION_AFTER_START : CKEDITOR.POSITION_AFTER_END );
 
-                                       // Avoid enlarging the range further when end boundary spans right after the BR. (#7490)
+                                       // Avoid enlarging the range further when end boundary spans right after the BR. (http://dev.ckeditor.com/ticket/7490)
                                        if ( unit == CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS ) {
                                                var theRange = this.clone();
                                                walker = new CKEDITOR.dom.walker( theRange );
@@ -1714,18 +1720,28 @@ CKEDITOR.dom.range = function( root ) {
                },
 
                /**
-                * Descrease the range to make sure that boundaries
-                * always anchor beside text nodes or innermost element.
+                * Decreases the range to make sure that boundaries
+                * always anchor beside text nodes or the innermost element.
                 *
                 * @param {Number} mode The shrinking mode ({@link CKEDITOR#SHRINK_ELEMENT} or {@link CKEDITOR#SHRINK_TEXT}).
                 *
-                * * {@link CKEDITOR#SHRINK_ELEMENT} - Shrink the range boundaries to the edge of the innermost element.
-                * * {@link CKEDITOR#SHRINK_TEXT} - Shrink the range boudaries to anchor by the side of enclosed text
-                *     node, range remains if there's no text nodes on boundaries at all.
-                *
-                * @param {Boolean} selectContents Whether result range anchors at the inner OR outer boundary of the node.
+                * * {@link CKEDITOR#SHRINK_ELEMENT} – Shrinks the range boundaries to the edge of the innermost element.
+                * * {@link CKEDITOR#SHRINK_TEXT} – Shrinks the range boundaries to anchor by the side of enclosed text
+                *     node. The range remains if there are no text nodes available on boundaries.
+                *
+                * @param {Boolean} [selectContents=false] Whether the resulting range anchors at the inner OR outer boundary of the node.
+                * @param {Boolean/Object} [options=true] If this parameter is of a Boolean type, it is treated as
+                * `options.shrinkOnBlockBoundary`. This parameter was added in 4.7.0.
+                * @param {Boolean} [options.shrinkOnBlockBoundary=true] Whether the block boundary should be included in
+                * the shrunk range.
+                * @param {Boolean} [options.skipBogus=false] Whether bogus `<br>` elements should be ignored while
+                * `mode` is set to {@link CKEDITOR#SHRINK_TEXT}. This option was added in 4.7.0.
                 */
-               shrink: function( mode, selectContents, shrinkOnBlockBoundary ) {
+               shrink: function( mode, selectContents, options ) {
+                       var shrinkOnBlockBoundary = typeof options === 'boolean' ? options :
+                               ( options && typeof options.shrinkOnBlockBoundary === 'boolean' ? options.shrinkOnBlockBoundary : true ),
+                               skipBogus = options && options.skipBogus;
+
                        // Unable to shrink a collapsed range.
                        if ( !this.collapsed ) {
                                mode = mode || CKEDITOR.SHRINK_TEXT;
@@ -1748,7 +1764,7 @@ CKEDITOR.dom.range = function( root ) {
                                                walkerRange.setStartAfter( startContainer );
                                        else {
                                                // Enlarge the range properly to avoid walker making
-                                               // DOM changes caused by triming the text nodes later.
+                                               // DOM changes caused by trimming the text nodes later.
                                                walkerRange.setStartBefore( startContainer );
                                                moveStart = 0;
                                        }
@@ -1766,7 +1782,8 @@ CKEDITOR.dom.range = function( root ) {
                                }
 
                                var walker = new CKEDITOR.dom.walker( walkerRange ),
-                                       isBookmark = CKEDITOR.dom.walker.bookmark();
+                                       isBookmark = CKEDITOR.dom.walker.bookmark(),
+                                       isBogus = CKEDITOR.dom.walker.bogus();
 
                                walker.evaluator = function( node ) {
                                        return node.type == ( mode == CKEDITOR.SHRINK_ELEMENT ? CKEDITOR.NODE_ELEMENT : CKEDITOR.NODE_TEXT );
@@ -1774,6 +1791,11 @@ CKEDITOR.dom.range = function( root ) {
 
                                var currentElement;
                                walker.guard = function( node, movingOut ) {
+                                       // Skipping bogus before other cases (http://dev.ckeditor.com/ticket/17010).
+                                       if ( skipBogus && isBogus( node ) ) {
+                                               return true;
+                                       }
+
                                        if ( isBookmark( node ) )
                                                return true;
 
@@ -1815,7 +1837,7 @@ CKEDITOR.dom.range = function( root ) {
 
                /**
                 * Inserts a node at the start of the range. The range will be expanded
-                * the contain the node.
+                * to contain the node.
                 *
                 * @param {CKEDITOR.dom.node} node
                 */
@@ -1842,7 +1864,7 @@ CKEDITOR.dom.range = function( root ) {
                },
 
                /**
-                * Moves the range to given position according to specified node.
+                * Moves the range to a given position according to the specified node.
                 *
                 *              // HTML: <p>Foo <b>bar</b></p>
                 *              range.moveToPosition( elB, CKEDITOR.POSITION_BEFORE_START );
@@ -1850,7 +1872,7 @@ CKEDITOR.dom.range = function( root ) {
                 *
                 * See also {@link #setStartAt} and {@link #setEndAt}.
                 *
-                * @param {CKEDITOR.dom.node} node The node according to which position will be set.
+                * @param {CKEDITOR.dom.node} node The node according to which the position will be set.
                 * @param {Number} position One of {@link CKEDITOR#POSITION_BEFORE_START},
                 * {@link CKEDITOR#POSITION_AFTER_START}, {@link CKEDITOR#POSITION_BEFORE_END},
                 * {@link CKEDITOR#POSITION_AFTER_END}.
@@ -2117,7 +2139,7 @@ CKEDITOR.dom.range = function( root ) {
                        // So even if the initial range was placed before the bogus <br>, after creating the bookmark it
                        // is placed before the bookmark.
                        // Fortunately, getBogus() is able to skip the bookmark so it finds the bogus <br> in this case.
-                       // We remove incorrectly placed one and add a brand new one. (#13001)
+                       // We remove incorrectly placed one and add a brand new one. (http://dev.ckeditor.com/ticket/13001)
                        var bogus = fixedBlock.getBogus();
                        if ( bogus ) {
                                bogus.remove();
@@ -2339,7 +2361,7 @@ CKEDITOR.dom.range = function( root ) {
                                        this.trim( 0, 1 );
                        }
 
-                       // Antecipate the trim() call here, so the walker will not make
+                       // Anticipate the trim() call here, so the walker will not make
                        // changes to the DOM, which would not get reflected into this
                        // range otherwise.
                        this.trim();
@@ -2378,7 +2400,7 @@ CKEDITOR.dom.range = function( root ) {
                                        this.trim( 1, 0 );
                        }
 
-                       // Antecipate the trim() call here, so the walker will not make
+                       // Anticipate the trim() call here, so the walker will not make
                        // changes to the DOM, which would not get reflected into this
                        // range otherwise.
                        this.trim();
@@ -2635,7 +2657,7 @@ CKEDITOR.dom.range = function( root ) {
                getEnclosedNode: function() {
                        var walkerRange = this.clone();
 
-                       // Optimize and analyze the range to avoid DOM destructive nature of walker. (#5780)
+                       // Optimize and analyze the range to avoid DOM destructive nature of walker. (http://dev.ckeditor.com/ticket/5780)
                        walkerRange.optimize();
                        if ( walkerRange.startContainer.type != CKEDITOR.NODE_ELEMENT || walkerRange.endContainer.type != CKEDITOR.NODE_ELEMENT )
                                return null;
@@ -2701,6 +2723,53 @@ CKEDITOR.dom.range = function( root ) {
                 */
                getPreviousEditableNode: getNextEditableNode( 1 ),
 
+               /**
+                * Returns any table element, like `td`, `tbody`, `table` etc. from a given range. The element
+                * is returned only if the range is contained within one table (might be a nested
+                * table, but it cannot be two different tables on the same DOM level).
+                *
+                * @private
+                * @since 4.7
+                * @param {Object} [tableElements] Mapping of element names that should be considered.
+                * @returns {CKEDITOR.dom.element/null}
+                */
+               _getTableElement: function( tableElements ) {
+                       tableElements = tableElements || {
+                               td: 1,
+                               th: 1,
+                               tr: 1,
+                               tbody: 1,
+                               thead: 1,
+                               tfoot: 1,
+                               table: 1
+                       };
+
+                       var start = this.startContainer,
+                               end = this.endContainer,
+                               startTable = start.getAscendant( 'table', true ),
+                               endTable = end.getAscendant( 'table', true );
+
+                       // Super weird edge case in Safari: if there is a table with only one cell inside and that cell
+                       // is selected, then the end boundary of the table is moved into editor's editable.
+                       // That case is also present when selecting the last cell inside nested table.
+                       if ( CKEDITOR.env.safari && startTable && end.equals( this.root ) ) {
+                               return start.getAscendant( tableElements, true );
+                       }
+
+                       if ( this.getEnclosedNode() ) {
+                               return this.getEnclosedNode().getAscendant( tableElements, true );
+                       }
+
+                       // Ensure that selection starts and ends in the same table or one of the table is inside the other.
+                       if ( startTable && endTable && ( startTable.equals( endTable ) || startTable.contains( endTable ) ||
+                               endTable.contains( startTable ) ) ) {
+
+                               return start.getAscendant( tableElements, true );
+                       }
+
+                       return null;
+               },
+
                /**
                 * Scrolls the start of current range into view.
                 */
@@ -2783,9 +2852,112 @@ CKEDITOR.dom.range = function( root ) {
                        }
                        // %REMOVE_END%
                        this.endContainer = endContainer;
+               },
+
+               /**
+                * Looks for elements matching the `query` selector within a range.
+                *
+                * @since 4.5.11
+                * @private
+                * @param {String} query
+                * @param {Boolean} [includeNonEditables=false] Whether elements with `contenteditable` set to `false` should
+                * be included.
+                * @returns {CKEDITOR.dom.element[]}
+                */
+               _find: function( query, includeNonEditables ) {
+                       var ancestor = this.getCommonAncestor(),
+                               boundaries = this.getBoundaryNodes(),
+                               // Contrary to CKEDITOR.dom.element#find we're returning array, that's because NodeList is immutable, and we need
+                               // to do some filtering in returned list.
+                               ret = [],
+                               curItem,
+                               i,
+                               initialMatches,
+                               isStartGood,
+                               isEndGood;
+
+                       if ( ancestor && ancestor.find ) {
+                               initialMatches = ancestor.find( query );
+
+                               for ( i = 0; i < initialMatches.count(); i++ ) {
+                                       curItem = initialMatches.getItem( i );
+
+                                       // Using isReadOnly() method to filterout non editables. It checks isContentEditable including all browser quirks.
+                                       if ( !includeNonEditables && curItem.isReadOnly() ) {
+                                               continue;
+                                       }
+
+                                       // It's not enough to get elements from common ancestor, because it might contain too many matches.
+                                       // We need to ensure that returned items are between boundary points.
+                                       isStartGood = ( curItem.getPosition( boundaries.startNode ) & CKEDITOR.POSITION_FOLLOWING ) || boundaries.startNode.equals( curItem );
+                                       isEndGood = ( curItem.getPosition( boundaries.endNode ) & ( CKEDITOR.POSITION_PRECEDING + CKEDITOR.POSITION_IS_CONTAINED ) ) || boundaries.endNode.equals( curItem );
+
+                                       if ( isStartGood && isEndGood ) {
+                                               ret.push( curItem );
+                                       }
+                               }
+                       }
+
+                       return ret;
                }
        };
 
+       /**
+        * Merges every subsequent range in given set, returning a smaller array of ranges.
+        *
+        * Note that each range in the returned value will be enlarged with `CKEDITOR.ENLARGE_ELEMENT` value.
+        *
+        * @since 4.7.0
+        * @static
+        * @param {CKEDITOR.dom.range[]} ranges
+        * @returns {CKEDITOR.dom.range[]} Set of merged ranges.
+        * @member CKEDITOR.dom.range
+        */
+       CKEDITOR.dom.range.mergeRanges = function( ranges ) {
+               return CKEDITOR.tools.array.reduce( ranges, function( ret, rng ) {
+                       // Last range ATM.
+                       var lastRange = ret[ ret.length - 1 ],
+                               isContinuation = false;
+
+                       // Make a clone, we don't want to modify input.
+                       rng = rng.clone();
+                       rng.enlarge( CKEDITOR.ENLARGE_ELEMENT );
+
+                       if ( lastRange ) {
+                               // The trick is to create a range spanning the gap between the two ranges. Then iterate over
+                               // each node found in this gap. If it contains anything other than whitespace, then it means it
+                               // is not a continuation.
+                               var gapRange = new CKEDITOR.dom.range( rng.root ),
+                                       walker = new CKEDITOR.dom.walker( gapRange ),
+                                       isWhitespace = CKEDITOR.dom.walker.whitespaces(),
+                                       nodeInBetween;
+
+                               gapRange.setStart( lastRange.endContainer, lastRange.endOffset );
+                               gapRange.setEnd( rng.startContainer, rng.startOffset );
+
+                               nodeInBetween = walker.next();
+
+                               while ( isWhitespace( nodeInBetween ) || rng.endContainer.equals( nodeInBetween ) ) {
+                                       // We don't care about whitespaces, and range container. Also we skip the endContainer,
+                                       // as it will also be provided by the iterator (as it visits it's opening tag).
+                                       nodeInBetween = walker.next();
+                               }
+
+                               // Simply, if anything has been found there's a content in between the two.
+                               isContinuation = !nodeInBetween;
+                       }
+
+                       if ( isContinuation ) {
+                               // If last range ends, where the current range starts, then let's merge it.
+                               lastRange.setEnd( rng.endContainer, rng.endOffset );
+                       } else {
+                               // In other case just push cur range into the stack.
+                               ret.push( rng );
+                       }
+
+                       return ret;
+               }, [] );
+       };
 
 } )();
 
@@ -2861,9 +3033,32 @@ CKEDITOR.POSITION_BEFORE_START = 3;
  */
 CKEDITOR.POSITION_AFTER_END = 4;
 
+/**
+ * @readonly
+ * @member CKEDITOR
+ * @property {Number} [=1]
+ */
 CKEDITOR.ENLARGE_ELEMENT = 1;
+
+/**
+ * @readonly
+ * @member CKEDITOR
+ * @property {Number} [=2]
+ */
 CKEDITOR.ENLARGE_BLOCK_CONTENTS = 2;
+
+/**
+ * @readonly
+ * @member CKEDITOR
+ * @property {Number} [=3]
+ */
 CKEDITOR.ENLARGE_LIST_ITEM_CONTENTS = 3;
+
+/**
+ * @readonly
+ * @member CKEDITOR
+ * @property {Number} [=4]
+ */
 CKEDITOR.ENLARGE_INLINE = 4;
 
 // Check boundary types.